Browse Source

More work on XAML styles.

- Added "Control[Property=Value]" selector handling
- Added hacks for ExpressionObserver not currently handing attached
properties
- Added ContentControl style
- Fixed ScrollBar and TextBox
Steven Kirk 10 years ago
parent
commit
142eaa1c7c

+ 9 - 0
samples/XamlTestApplicationPcl/ContentControl.paml

@@ -0,0 +1,9 @@
+<Style xmlns="https://github.com/perspex" Selector="ContentControl">
+  <Setter Property="Template">
+    <ControlTemplate>
+      <Border Background="{TemplateBinding Background}">
+        <ContentPresenter Content="{TemplateBinding Content}" Margin="{TemplateBinding Padding}"/>
+      </Border>
+    </ControlTemplate>
+  </Setter>
+</Style>

+ 13 - 1
samples/XamlTestApplicationPcl/ScrollBar.paml

@@ -8,7 +8,7 @@
                  Value="{TemplateBinding Path=Value, Mode=TwoWay}"
                  ViewportSize="{TemplateBinding ViewportSize}"
                  Orientation="{TemplateBinding Orientation}">
-            <Thumb>
+            <Thumb Name="thumb">
               <Thumb.Template>
                 <ControlTemplate>
                   <Border Background="Gray"/>
@@ -20,4 +20,16 @@
       </ControlTemplate>
     </Setter>
   </Style>
+  <Style Selector="ScrollBar[Orientation=Horizontal]">
+    <Setter Property="Height" Value="10"/>
+  </Style>
+  <Style Selector="ScrollBar[Orientation=Horizontal] /template/ Thumb#thumb">
+    <Setter Property="MinWidth" Value="10"/>
+  </Style>
+  <Style Selector="ScrollBar[Orientation=Vertical]">
+    <Setter Property="Width" Value="10"/>
+  </Style>
+  <Style Selector="ScrollBar[Orientation=Vertical] /template/ Thumb#thumb">
+    <Setter Property="MinHeight" Value="10"/>
+  </Style>
 </Styles>

+ 3 - 0
samples/XamlTestApplicationPcl/XamlTestApplicationPcl.csproj

@@ -164,6 +164,9 @@
     <EmbeddedResource Include="TextBox.paml">
       <SubType>Designer</SubType>
     </EmbeddedResource>
+    <EmbeddedResource Include="ContentControl.paml">
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
   </ItemGroup>
   <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 31 - 1
src/Markup/Perspex.Markup.Xaml/Parsers/SelectorParser.cs

@@ -2,7 +2,9 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.Globalization;
 using Perspex.Styling;
+using Perspex.Utilities;
 using Sprache;
 
 namespace Perspex.Markup.Xaml.Parsers
@@ -61,7 +63,35 @@ namespace Perspex.Markup.Xaml.Parsers
                 }
                 else if (property != null)
                 {
-                    throw new NotImplementedException();
+                    var type = result.TargetType;
+
+                    if (type == null)
+                    {
+                        throw new InvalidOperationException("Property selectors must be applied to a type.");
+                    }
+
+                    var targetProperty = PerspexPropertyRegistry.Instance.FindRegistered(type, property.Property);
+
+                    if (targetProperty == null)
+                    {
+                        throw new InvalidOperationException($"Cannot find '{property.Property}' on '{type}");
+                    }
+
+                    object typedValue;
+
+                    if (TypeUtilities.TryConvert(
+                            targetProperty.PropertyType, 
+                            property.Value, 
+                            CultureInfo.InvariantCulture,
+                            out typedValue))
+                    {
+                        result = result.PropertyEquals(targetProperty, typedValue);
+                    }
+                    else
+                    {
+                        throw new InvalidOperationException(
+                            $"Could not convert '{property.Value}' to '{targetProperty.PropertyType}");
+                    }
                 }
                 else if (child != null)
                 {

+ 13 - 2
src/Perspex.Base/Utilities/TypeUtilities.cs

@@ -91,7 +91,7 @@ namespace Perspex.Utilities
 
         /// <summary>
         /// Try to convert a value to a type, using <see cref="System.Convert"/> if possible,
-        /// otherwise using <see cref="TryCast(Type, object, out object, bool)"/>.
+        /// otherwise using <see cref="TryCast(Type, object, out object)"/>.
         /// </summary>
         /// <param name="to">The type to cast to.</param>
         /// <param name="value">The value to cast.</param>
@@ -100,7 +100,18 @@ namespace Perspex.Utilities
         /// <returns>True if the cast was sucessful, otherwise false.</returns>
         public static bool TryConvert(Type to, object value, CultureInfo culture, out object result)
         {
-            if ((value.GetType() == typeof(string) && Conversions.ContainsKey(to)) ||
+            var valueType = value.GetType();
+
+            if (to.GetTypeInfo().IsEnum && valueType == typeof(string))
+            {
+                if (Enum.IsDefined(to, (string)value))
+                {
+                    result = Enum.Parse(to, (string)value);
+                    return true;
+                }
+            }
+
+            if ((valueType == typeof(string) && Conversions.ContainsKey(to)) ||
                 (to == typeof(string) && Conversions.ContainsKey(value.GetType())))
             {
                 try

+ 7 - 0
src/Perspex.Controls/ContentControl.cs

@@ -34,6 +34,13 @@ namespace Perspex.Controls
         public static readonly PerspexProperty<VerticalAlignment> VerticalContentAlignmentProperty =
             PerspexProperty.Register<ContentControl, VerticalAlignment>(nameof(VerticalContentAlignment));
 
+        /// <summary>
+        /// Initializes static members of the <see cref="Button"/> class.
+        /// </summary>
+        static ContentControl()
+        {
+        }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ContentControl"/> class.
         /// </summary>

+ 42 - 0
src/Perspex.Controls/ScrollViewer.cs

@@ -112,12 +112,54 @@ namespace Perspex.Controls
             set { SetValue(CanScrollHorizontallyProperty, value); }
         }
 
+        // HACK: Currently exposed because XAML bindings don't work with attached perspex properties.
+        public double HorizontalScrollBarMaximum
+        {
+            get { return GetValue(HorizontalScrollBarMaximumProperty); }
+            private set { SetValue(HorizontalScrollBarMaximumProperty, value); }
+        }
+
+        // HACK: Currently exposed because XAML bindings don't work with attached perspex properties.
+        public double HorizontalScrollBarValue
+        {
+            get { return GetValue(HorizontalScrollBarValueProperty); }
+            private set { SetValue(HorizontalScrollBarValueProperty, value); }
+        }
+
+        // HACK: Currently exposed because XAML bindings don't work with attached perspex properties.
+        public double HorizontalScrollBarViewportSize
+        {
+            get { return GetValue(HorizontalScrollBarViewportSizeProperty); }
+            set { SetValue(HorizontalScrollBarViewportSizeProperty, value); }
+        }
+
         public ScrollBarVisibility HorizontalScrollBarVisibility
         {
             get { return GetValue(HorizontalScrollBarVisibilityProperty); }
             set { SetValue(HorizontalScrollBarVisibilityProperty, value); }
         }
 
+        // HACK: Currently exposed because XAML bindings don't work with attached perspex properties.
+        public double VerticalScrollBarMaximum
+        {
+            get { return GetValue(VerticalScrollBarMaximumProperty); }
+            private set { SetValue(VerticalScrollBarMaximumProperty, value); }
+        }
+
+        // HACK: Currently exposed because XAML bindings don't work with attached perspex properties.
+        public double VerticalScrollBarValue
+        {
+            get { return GetValue(VerticalScrollBarValueProperty); }
+            private set { SetValue(VerticalScrollBarValueProperty, value); }
+        }
+
+        // HACK: Currently exposed because XAML bindings don't work with attached perspex properties.
+        public double VerticalScrollBarViewportSize
+        {
+            get { return GetValue(VerticalScrollBarViewportSizeProperty); }
+            private set { SetValue(VerticalScrollBarViewportSizeProperty, value); }
+        }
+
         public ScrollBarVisibility VerticalScrollBarVisibility
         {
             get { return GetValue(VerticalScrollBarVisibilityProperty); }

+ 21 - 0
src/Perspex.Controls/TextBox.cs

@@ -127,6 +127,27 @@ namespace Perspex.Controls
             set { SetValue(TextWrappingProperty, value); }
         }
 
+        // HACK: Currently exposed because XAML bindings don't work with attached perspex properties.
+        public bool CanScrollHorizontally
+        {
+            get { return GetValue(ScrollViewer.CanScrollHorizontallyProperty); }
+            set { SetValue(ScrollViewer.CanScrollHorizontallyProperty, value); }
+        }
+
+        // HACK: Currently exposed because XAML bindings don't work with attached perspex properties.
+        public ScrollBarVisibility HorizontalScrollBarVisibility
+        {
+            get { return GetValue(ScrollViewer.HorizontalScrollBarVisibilityProperty); }
+            set { SetValue(ScrollViewer.HorizontalScrollBarVisibilityProperty, value); }
+        }
+
+        // HACK: Currently exposed because XAML bindings don't work with attached perspex properties.
+        public ScrollBarVisibility VerticalScrollBarVisibility
+        {
+            get { return GetValue(ScrollViewer.VerticalScrollBarVisibilityProperty); }
+            set { SetValue(ScrollViewer.VerticalScrollBarVisibilityProperty, value); }
+        }
+
         protected override void OnTemplateApplied()
         {
             _presenter = this.GetTemplateChild<TextPresenter>("PART_TextPresenter");

+ 17 - 1
src/Perspex.Styling/Styling/Selectors.cs

@@ -128,6 +128,22 @@ namespace Perspex.Styling
             return new Selector(previous, x => MatchPropertyEquals(x, property, value), $"[{property.Name}={value}]");
         }
 
+        /// <summary>
+        /// Returns a selector which matches a control with the specified property value.
+        /// </summary>
+        /// <typeparam name="T">The property type.</typeparam>
+        /// <param name="previous">The previous selector.</param>
+        /// <param name="property">The property.</param>
+        /// <param name="value">The property value.</param>
+        /// <returns>The selector.</returns>
+        public static Selector PropertyEquals(this Selector previous, PerspexProperty property, object value)
+        {
+            Contract.Requires<ArgumentNullException>(previous != null);
+            Contract.Requires<ArgumentNullException>(property != null);
+
+            return new Selector(previous, x => MatchPropertyEquals(x, property, value), $"[{property.Name}={value}]");
+        }
+
         /// <summary>
         /// Returns a selector which enters a lookless control's template.
         /// </summary>
@@ -216,7 +232,7 @@ namespace Perspex.Styling
             return new SelectorMatch(controlType == type);
         }
 
-        private static SelectorMatch MatchPropertyEquals<T>(IStyleable x, PerspexProperty<T> property, object value)
+        private static SelectorMatch MatchPropertyEquals(IStyleable x, PerspexProperty property, object value)
         {
             if (!x.IsRegistered(property))
             {