Stylesheets (#1207)
* [*] Support for CSS StyleSheets Parsing, loading, and applying of StyleSheets see https://github.com/xamarin/Xamarin.Forms/pull/1207#issue-266464280 for complete description. * use initial, not none * fix tests
This commit is contained in:
Родитель
2eba087a75
Коммит
5ea86a266b
|
@ -1,7 +1,10 @@
|
|||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<ItemGroup Condition="'$(EnableDefaultItems)'=='True' And '$(EnableDefaultXamlItems)'=='True' And '$(EnableDefaultEmbeddedResourceItems)'=='True'">
|
||||
<EmbeddedResource Include="**\*.xaml" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" SubType="Designer" Generator="MSBuild:UpdateDesignTimeXaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(EnableDefaultItems)'=='True' And '$(EnableDefaultXamlItems)'=='True' And '$(EnableDefaultEmbeddedResourceItems)'=='True'">
|
||||
<EmbeddedResource Include="**\*.xaml" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" SubType="Designer" Generator="MSBuild:UpdateDesignTimeXaml" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
<ItemGroup Condition="'$(EnableDefaultItems)'=='True' And '$(EnableDefaultCssItems)'=='True' And '$(EnableDefaultEmbeddedResourceItems)'=='True'">
|
||||
<EmbeddedResource Include="**\*.css" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -11,5 +11,8 @@
|
|||
<Compile Update="**\*.xaml$(DefaultLanguageSourceExtension)" DependentUpon="%(Filename)" SubType="Code" />
|
||||
<None Remove="**\*.xaml" Condition="'$(EnableDefaultNoneItems)'=='True'" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(EnableDefaultItems)'=='True' And '$(EnableDefaultCssItems)'=='True' And '$(EnableDefaultEmbeddedResourceItems)'=='True'">
|
||||
<None Remove="**\*.xaml" Condition="'$(EnableDefaultNoneItems)'=='True'" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,12 +1,15 @@
|
|||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<UsingTask TaskName="Xamarin.Forms.Build.Tasks.XamlGTask" AssemblyFile="$(MSBuildThisFileDirectory)Xamarin.Forms.Build.Tasks.dll" />
|
||||
<UsingTask TaskName="Xamarin.Forms.Build.Tasks.CssGTask" AssemblyFile="$(MSBuildThisFileDirectory)Xamarin.Forms.Build.Tasks.dll"/>
|
||||
<UsingTask TaskName="Xamarin.Forms.Build.Tasks.XamlCTask" AssemblyFile="$(MSBuildThisFileDirectory)Xamarin.Forms.Build.Tasks.dll" />
|
||||
<UsingTask TaskName="Xamarin.Forms.Build.Tasks.GetTasksAbi" AssemblyFile="$(MSBuildThisFileDirectory)Xamarin.Forms.Build.Tasks.dll" />
|
||||
|
||||
<PropertyGroup>
|
||||
<EnableDefaultXamlItems Condition="'$(EnableDefaultXamlItems)'==''">True</EnableDefaultXamlItems>
|
||||
<_DefaultXamlItemsEnabled>False</_DefaultXamlItemsEnabled>
|
||||
<EnableDefaultCssItems Condition="'$(EnableDefaultCssItems)'==''">True</EnableDefaultCssItems>
|
||||
<_DefaultCssItemsEnabled>False</_DefaultCssItemsEnabled>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(MSBuildThisFileDirectory)Xamarin.Forms.DefaultItems.targets" Condition="'$(MSBuildSDKsPath)'!=''" />
|
||||
|
@ -34,7 +37,7 @@
|
|||
</GetTasksAbi >
|
||||
|
||||
<PropertyGroup>
|
||||
<_XFTasksExpectedAbi>3</_XFTasksExpectedAbi>
|
||||
<_XFTasksExpectedAbi>4</_XFTasksExpectedAbi>
|
||||
</PropertyGroup>
|
||||
|
||||
<Error
|
||||
|
@ -96,4 +99,25 @@
|
|||
DebugType = "$(DebugType)"
|
||||
KeepXamlResources = "$(XFKeepXamlResources)" />
|
||||
</Target>
|
||||
|
||||
<!-- CssG -->
|
||||
<PropertyGroup>
|
||||
<CoreCompileDependsOn>
|
||||
CssG;
|
||||
$(CoreCompileDependsOn);
|
||||
</CoreCompileDependsOn>
|
||||
</PropertyGroup>
|
||||
<Target Name="CssG" BeforeTargets="BeforeCompile" Condition="'$(_CssGAlreadyExecuted)'!='true'">
|
||||
<PropertyGroup>
|
||||
<_CssGAlreadyExecuted>true</_CssGAlreadyExecuted>
|
||||
</PropertyGroup>
|
||||
<CssGTask
|
||||
XamlFiles="@(EmbeddedResource)" Condition="'%(Extension)' == '.css' AND '$(DefaultLanguageSourceExtension)' == '.cs'"
|
||||
Language = "$(Language)"
|
||||
AssemblyName = "$(AssemblyName)"
|
||||
OutputPath = "$(IntermediateOutputPath)">
|
||||
<Output ItemName="FilesWrite" TaskParameter="GeneratedCodeFiles" />
|
||||
<Output ItemName="Compile" TaskParameter="GeneratedCodeFiles" />
|
||||
</CssGTask>
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
@ -66,5 +65,17 @@ namespace Xamarin.Forms.Core.XamlC
|
|||
//ldloc the stored uri as return value
|
||||
yield return Create(Ldloc, uriVarDef);
|
||||
}
|
||||
|
||||
internal static string GetPathForType(ModuleDefinition module, TypeReference type)
|
||||
{
|
||||
foreach (var ca in type.Module.GetCustomAttributes()) {
|
||||
if (!TypeRefComparer.Default.Equals(ca.AttributeType, module.ImportReference(typeof(XamlResourceIdAttribute))))
|
||||
continue;
|
||||
if (!TypeRefComparer.Default.Equals(ca.ConstructorArguments[2].Value as TypeReference, type))
|
||||
continue;
|
||||
return ca.ConstructorArguments[1].Value as string;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using System.Xml;
|
||||
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
using Xamarin.Forms.Build.Tasks;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
using static Mono.Cecil.Cil.Instruction;
|
||||
using static Mono.Cecil.Cil.OpCodes;
|
||||
|
||||
|
||||
namespace Xamarin.Forms.Core.XamlC
|
||||
{
|
||||
class StyleSheetProvider : ICompiledValueProvider
|
||||
{
|
||||
public IEnumerable<Instruction> ProvideValue(VariableDefinitionReference vardefref, ModuleDefinition module, BaseNode node, ILContext context)
|
||||
{
|
||||
INode sourceNode = null;
|
||||
((IElementNode)node).Properties.TryGetValue(new XmlName("", "Source"), out sourceNode);
|
||||
if (sourceNode == null)
|
||||
((IElementNode)node).Properties.TryGetValue(new XmlName(XamlParser.XFUri, "Source"), out sourceNode);
|
||||
|
||||
INode styleNode = null;
|
||||
if (!((IElementNode)node).Properties.TryGetValue(new XmlName("", "Style"), out styleNode) &&
|
||||
!((IElementNode)node).Properties.TryGetValue(new XmlName(XamlParser.XFUri, "Style"), out styleNode) &&
|
||||
((IElementNode)node).CollectionItems.Count == 1)
|
||||
styleNode = ((IElementNode)node).CollectionItems[0];
|
||||
|
||||
if (sourceNode != null && styleNode != null)
|
||||
throw new XamlParseException($"StyleSheet can not have both a Source and a content", node);
|
||||
|
||||
if (sourceNode == null && styleNode == null)
|
||||
throw new XamlParseException($"StyleSheet require either a Source or a content", node);
|
||||
|
||||
if (styleNode != null && !(styleNode is ValueNode))
|
||||
throw new XamlParseException($"Style property or Content is not a string literal", node);
|
||||
|
||||
if (sourceNode != null && !(sourceNode is ValueNode))
|
||||
throw new XamlParseException($"Source property is not a string literal", node);
|
||||
|
||||
if (styleNode != null) {
|
||||
var style = (styleNode as ValueNode).Value as string;
|
||||
yield return Create(Ldstr, style);
|
||||
|
||||
var fromString = module.ImportReference(typeof(StyleSheets.StyleSheet).GetMethods().FirstOrDefault(mi => mi.Name == nameof(StyleSheets.StyleSheet.FromString) && mi.GetParameters().Length == 1));
|
||||
yield return Create(Call, module.ImportReference(fromString));
|
||||
}
|
||||
else {
|
||||
string source = (sourceNode as ValueNode)?.Value as string;
|
||||
INode rootNode = node;
|
||||
while (!(rootNode is ILRootNode))
|
||||
rootNode = rootNode.Parent;
|
||||
|
||||
var rootTargetPath = RDSourceTypeConverter.GetPathForType(module, ((ILRootNode)rootNode).TypeReference);
|
||||
var uri = new Uri(source, UriKind.Relative);
|
||||
|
||||
var resourcePath = ResourceDictionary.RDSourceTypeConverter.GetResourcePath(uri, rootTargetPath);
|
||||
//fail early
|
||||
var resourceId = XamlCTask.GetResourceIdForPath(module, resourcePath);
|
||||
if (resourceId == null)
|
||||
throw new XamlParseException($"Resource '{source}' not found.", node);
|
||||
|
||||
var getTypeFromHandle = module.ImportReference(typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle), new[] { typeof(RuntimeTypeHandle) }));
|
||||
var getAssembly = module.ImportReference(typeof(Type).GetProperty(nameof(Type.Assembly)).GetGetMethod());
|
||||
yield return Create(Ldtoken, module.ImportReference(((ILRootNode)rootNode).TypeReference));
|
||||
yield return Create(Call, module.ImportReference(getTypeFromHandle));
|
||||
yield return Create(Callvirt, module.ImportReference(getAssembly)); //assembly
|
||||
|
||||
yield return Create(Ldstr, resourceId); //resourceId
|
||||
|
||||
foreach (var instruction in node.PushXmlLineInfo(context))
|
||||
yield return instruction; //lineinfo
|
||||
|
||||
var fromAssemblyResource = module.ImportReference(typeof(StyleSheets.StyleSheet).GetMethods().FirstOrDefault(mi => mi.Name == nameof(StyleSheets.StyleSheet.FromAssemblyResource) && mi.GetParameters().Length == 3));
|
||||
yield return Create(Call, module.ImportReference(fromAssemblyResource));
|
||||
}
|
||||
|
||||
//the variable is of type `object`. fix that
|
||||
var vardef = new VariableDefinition(module.ImportReference(typeof(StyleSheets.StyleSheet)));
|
||||
yield return Create(Stloc, vardef);
|
||||
vardefref.VariableDefinition = vardef;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Xamarin.Forms.Build.Tasks
|
||||
{
|
||||
public class CssGTask : Task
|
||||
{
|
||||
readonly List<ITaskItem> _generatedCodeFiles = new List<ITaskItem>();
|
||||
|
||||
[Required]
|
||||
public ITaskItem[] XamlFiles { get; set; }
|
||||
|
||||
[Output]
|
||||
public ITaskItem[] GeneratedCodeFiles => _generatedCodeFiles.ToArray();
|
||||
|
||||
public string Language { get; set; }
|
||||
public string AssemblyName { get; set; }
|
||||
public string OutputPath { get; set; }
|
||||
|
||||
public override bool Execute()
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if (XamlFiles == null) {
|
||||
Log.LogMessage("Skipping CssG");
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var xamlFile in XamlFiles) {
|
||||
var outputFile = Path.Combine(OutputPath, $"{xamlFile.GetMetadata("TargetPath")}.g.cs");
|
||||
var generator = new CssGenerator(xamlFile, Language, AssemblyName, outputFile, Log);
|
||||
try {
|
||||
if (generator.Execute())
|
||||
_generatedCodeFiles.Add(new TaskItem(Microsoft.Build.Evaluation.ProjectCollection.Escape(outputFile)));
|
||||
}
|
||||
catch (XmlException xe) {
|
||||
Log.LogError(null, null, null, xamlFile.ItemSpec, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
|
||||
|
||||
success = false;
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.LogError(null, null, null, xamlFile.ItemSpec, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
using System.CodeDom;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.IO;
|
||||
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
using Microsoft.CSharp;
|
||||
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Build.Tasks
|
||||
{
|
||||
class CssGenerator
|
||||
{
|
||||
internal CssGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
public CssGenerator(
|
||||
ITaskItem taskItem,
|
||||
string language,
|
||||
string assemblyName,
|
||||
string outputFile,
|
||||
TaskLoggingHelper logger)
|
||||
: this(
|
||||
taskItem.ItemSpec,
|
||||
language,
|
||||
taskItem.GetMetadata("ManifestResourceName"),
|
||||
taskItem.GetMetadata("TargetPath"),
|
||||
assemblyName,
|
||||
outputFile,
|
||||
logger)
|
||||
{
|
||||
}
|
||||
|
||||
internal static CodeDomProvider Provider = new CSharpCodeProvider();
|
||||
|
||||
public string CssFile { get; }
|
||||
public string Language { get; }
|
||||
public string ResourceId { get; }
|
||||
public string TargetPath { get; }
|
||||
public string AssemblyName { get; }
|
||||
public string OutputFile { get; }
|
||||
public TaskLoggingHelper Logger { get; }
|
||||
|
||||
public CssGenerator(
|
||||
string cssFile,
|
||||
string language,
|
||||
string resourceId,
|
||||
string targetPath,
|
||||
string assemblyName,
|
||||
string outputFile,
|
||||
TaskLoggingHelper logger = null)
|
||||
{
|
||||
CssFile = cssFile;
|
||||
Language = language;
|
||||
ResourceId = resourceId;
|
||||
TargetPath = targetPath;
|
||||
AssemblyName = assemblyName;
|
||||
OutputFile = outputFile;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
//returns true if a file is generated
|
||||
public bool Execute()
|
||||
{
|
||||
Logger?.LogMessage("Source: {0}", CssFile);
|
||||
Logger?.LogMessage(" Language: {0}", Language);
|
||||
Logger?.LogMessage(" ResourceID: {0}", ResourceId);
|
||||
Logger?.LogMessage(" TargetPath: {0}", TargetPath);
|
||||
Logger?.LogMessage(" AssemblyName: {0}", AssemblyName);
|
||||
Logger?.LogMessage(" OutputFile {0}", OutputFile);
|
||||
|
||||
GenerateCode();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GenerateCode()
|
||||
{
|
||||
//Create the target directory if required
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(OutputFile));
|
||||
|
||||
var ccu = new CodeCompileUnit();
|
||||
ccu.AssemblyCustomAttributes.Add(
|
||||
new CodeAttributeDeclaration(new CodeTypeReference($"global::{typeof(XamlResourceIdAttribute).FullName}"),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(ResourceId)),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(TargetPath.Replace('\\', '/'))),
|
||||
new CodeAttributeArgument(new CodePrimitiveExpression(null))
|
||||
));
|
||||
|
||||
//write the result
|
||||
using (var writer = new StreamWriter(OutputFile))
|
||||
Provider.GenerateCodeFromCompileUnit(ccu, writer, new CodeGeneratorOptions());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
public class GetTasksAbi : Task
|
||||
{
|
||||
[Output]
|
||||
public string AbiVersion { get; } = "3";
|
||||
public string AbiVersion { get; } = "4";
|
||||
|
||||
public override bool Execute()
|
||||
=> true;
|
||||
|
|
|
@ -24,8 +24,10 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
Context.Scopes[node] = Context.Scopes[parentNode];
|
||||
if (IsXNameProperty(node, parentNode))
|
||||
RegisterName((string)node.Value, Context.Scopes[node].Item1, Context.Scopes[node].Item2, Context.Variables[(IElementNode)parentNode], node);
|
||||
if (!IsXNameProperty(node, parentNode))
|
||||
return;
|
||||
RegisterName((string)node.Value, Context.Scopes[node].Item1, Context.Scopes[node].Item2, Context.Variables[(IElementNode)parentNode], node);
|
||||
SetStyleId((string)node.Value, Context.Variables[(IElementNode)parentNode]);
|
||||
}
|
||||
|
||||
public void Visit(MarkupNode node, INode parentNode)
|
||||
|
@ -123,7 +125,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
var module = Context.Body.Method.Module;
|
||||
var nsRef = module.ImportReference(typeof (INameScope));
|
||||
var nsDef = nsRef.Resolve();
|
||||
var registerInfo = nsDef.Methods.First(md => md.Name == "RegisterName" && md.Parameters.Count == 2);
|
||||
var registerInfo = nsDef.Methods.First(md => md.Name == nameof(INameScope.RegisterName) && md.Parameters.Count == 2);
|
||||
var register = module.ImportReference(registerInfo);
|
||||
|
||||
Context.IL.Emit(OpCodes.Ldloc, namescopeVarDef);
|
||||
|
@ -131,5 +133,26 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
Context.IL.Emit(OpCodes.Ldloc, element);
|
||||
Context.IL.Emit(OpCodes.Callvirt, register);
|
||||
}
|
||||
|
||||
void SetStyleId(string str, VariableDefinition element)
|
||||
{
|
||||
if (!element.VariableType.InheritsFromOrImplements(Context.Body.Method.Module.ImportReference(typeof(Element))))
|
||||
return;
|
||||
|
||||
var module = Context.Body.Method.Module;
|
||||
var eltDef = module.ImportReference(typeof(Element)).Resolve();
|
||||
var styleIdInfo = eltDef.Properties.First(pd => pd.Name == nameof(Element.StyleId));
|
||||
var getStyleId = module.ImportReference(styleIdInfo.GetMethod);
|
||||
var setStyleId = module.ImportReference(styleIdInfo.SetMethod);
|
||||
|
||||
var nop = Instruction.Create(OpCodes.Nop);
|
||||
Context.IL.Emit(OpCodes.Ldloc, element);
|
||||
Context.IL.Emit(OpCodes.Callvirt, getStyleId);
|
||||
Context.IL.Emit(OpCodes.Brtrue, nop);
|
||||
Context.IL.Emit(OpCodes.Ldloc, element);
|
||||
Context.IL.Emit(OpCodes.Ldstr, str);
|
||||
Context.IL.Emit(OpCodes.Callvirt, setStyleId);
|
||||
Context.IL.Append(nop);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1144,16 +1144,20 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
static bool CanAddToResourceDictionary(TypeReference collectionType, IElementNode node, IXmlLineInfo lineInfo, ILContext context)
|
||||
{
|
||||
if ( collectionType.FullName != "Xamarin.Forms.ResourceDictionary"
|
||||
&& collectionType.Resolve().BaseType?.FullName != "Xamarin.Forms.ResourceDictionary")
|
||||
&& collectionType.Resolve().BaseType?.FullName != "Xamarin.Forms.ResourceDictionary")
|
||||
return false;
|
||||
|
||||
|
||||
if (node.Properties.ContainsKey(XmlName.xKey))
|
||||
return true;
|
||||
|
||||
if (node.XmlType.Name == "Style")
|
||||
return true;
|
||||
|
||||
if (node.XmlType.Name == "ResourceDictionary")
|
||||
//is there a RD.Add() overrides that accepts this ?
|
||||
var nodeTypeRef = context.Variables[node].VariableType;
|
||||
var module = context.Body.Method.Module;
|
||||
if (module.ImportReference(typeof(ResourceDictionary)).Resolve().Methods.Any(md =>
|
||||
md.Name == "Add"
|
||||
&& md.Parameters.Count == 1
|
||||
&& TypeRefComparer.Default.Equals(md.Parameters[0].ParameterType, nodeTypeRef)))
|
||||
return true;
|
||||
|
||||
throw new XamlParseException("resources in ResourceDictionary require a x:Key attribute", lineInfo);
|
||||
|
@ -1212,7 +1216,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
yield break;
|
||||
}
|
||||
|
||||
var nodeTypeRef = node.XmlType.GetTypeReference(module, lineInfo);
|
||||
var nodeTypeRef = context.Variables[node].VariableType;
|
||||
yield return Create(Ldloc, context.Variables[node]);
|
||||
yield return Create(Callvirt,
|
||||
module.ImportReference(
|
||||
|
|
|
@ -28,4 +28,4 @@
|
|||
<Copy SourceFiles="@(_CopyItems)" DestinationFolder="..\.nuspec\" ContinueOnError="true" Retries="0" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
{
|
||||
Title = "Issue 181";
|
||||
Content = new Frame {
|
||||
OutlineColor = Color.Red,
|
||||
BorderColor = Color.Red,
|
||||
BackgroundColor = new Color (1.0, 1.0, 0.0),
|
||||
Content = new Label {
|
||||
Text = "I should have red text",
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace Xamarin.Forms.Controls
|
|||
{
|
||||
var bvBackground = new Frame {
|
||||
Content = new Label { Text = "" },
|
||||
OutlineColor = Color.FromRgb (0x06, 0x68, 0xCF),
|
||||
BorderColor = Color.FromRgb (0x06, 0x68, 0xCF),
|
||||
BackgroundColor = Color.FromRgba (0f, 0f, 0f, 0.4f),
|
||||
HasShadow = true
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Xamarin.Forms.Controls
|
|||
{
|
||||
Frame frPatientInfo = new Frame
|
||||
{
|
||||
OutlineColor = Color.Black,
|
||||
BorderColor = Color.Black,
|
||||
BackgroundColor = Color.White,
|
||||
HasShadow = true,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace Xamarin.Forms.Controls
|
|||
{
|
||||
element.HeightRequest = 50;
|
||||
element.WidthRequest = 100;
|
||||
element.OutlineColor = Color.Olive;
|
||||
element.BorderColor = Color.Olive;
|
||||
}
|
||||
|
||||
protected override void Build (StackLayout stackLayout)
|
||||
|
@ -22,9 +22,9 @@ namespace Xamarin.Forms.Controls
|
|||
base.Build (stackLayout);
|
||||
|
||||
var hasShadowContainer = new StateViewContainer<Frame> (Test.Frame.HasShadow, new Frame { HasShadow = true });
|
||||
var outlineColorContainer = new StateViewContainer<Frame> (Test.Frame.OutlineColor, new Frame { OutlineColor = Color.Teal, });
|
||||
var outlineColorContainer = new StateViewContainer<Frame> (Test.Frame.OutlineColor, new Frame { BorderColor = Color.Teal, });
|
||||
var viewContainer = new StateViewContainer<Frame> (Test.Frame.OutlineColor, new Frame {
|
||||
OutlineColor = Color.Teal,
|
||||
BorderColor = Color.Teal,
|
||||
Content = new Label { Text = "I am a frame" }
|
||||
});
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace Xamarin.Forms.Controls
|
|||
|
||||
var entry = new Entry { Placeholder = "Edit this text on entry", PlaceholderColor = Color.Pink, TextColor = Color.Yellow, BackgroundColor = Color.Green };
|
||||
|
||||
var frame = new Frame { HasShadow = true, BackgroundColor = Color.Maroon, OutlineColor = Color.Lime, MinimumHeightRequest = 100 };
|
||||
var frame = new Frame { HasShadow = true, BackgroundColor = Color.Maroon, BorderColor = Color.Lime, MinimumHeightRequest = 100 };
|
||||
|
||||
|
||||
var image = new Image { HeightRequest = 100, Source = "crimson.jpg" };
|
||||
|
|
|
@ -189,8 +189,7 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
public void ManuallySetFontSizeNotOverridenByStyle ()
|
||||
{
|
||||
var label = new Label ();
|
||||
|
||||
Assert.AreEqual (10.0, label.FontSize);
|
||||
Assume.That (label.FontSize, Is.EqualTo(10.0));
|
||||
|
||||
label.SetValue (Label.FontSizeProperty, 2.0, false);
|
||||
Assert.AreEqual (2.0, label.FontSize);
|
||||
|
@ -199,6 +198,19 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
Assert.AreEqual (2.0, label.FontSize);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ManuallySetFontSizeNotOverridenByFontSetInStyle()
|
||||
{
|
||||
var label = new Label();
|
||||
Assume.That(label.FontSize, Is.EqualTo(10.0));
|
||||
|
||||
label.SetValue(Label.FontSizeProperty, 2.0);
|
||||
Assert.AreEqual(2.0, label.FontSize);
|
||||
|
||||
label.SetValue(Label.FontProperty, Font.SystemFontOfSize(1.0), fromStyle: true);
|
||||
Assert.AreEqual(2.0, label.FontSize);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChangingHorizontalTextAlignmentFiresXAlignChanged ()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
using Xamarin.Forms.Core.UnitTests;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class IStylableTest
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Device.PlatformServices = new MockPlatformServices();
|
||||
Internals.Registrar.RegisterAll(new Type[0]);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GetPropertyDefinedOnParent()
|
||||
{
|
||||
var label = new Label();
|
||||
var bp = ((IStylable)label).GetProperty("background-color");
|
||||
Assert.AreSame(VisualElement.BackgroundColorProperty, bp);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GetPropertyDefinedOnType()
|
||||
{
|
||||
var label = new Label();
|
||||
var bp = ((IStylable)label).GetProperty("color");
|
||||
Assert.AreSame(Label.TextColorProperty, bp);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GetPropertyDefinedOnType2()
|
||||
{
|
||||
var entry = new Entry();
|
||||
var bp = ((IStylable)entry).GetProperty("color");
|
||||
Assert.AreSame(Entry.TextColorProperty, bp);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GetInvalidPropertyForType()
|
||||
{
|
||||
var grid = new Grid();
|
||||
var bp = ((IStylable)grid).GetProperty("color");
|
||||
Assert.Null(bp);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GetPropertyDefinedOnPropertyOwnerType()
|
||||
{
|
||||
var frame = new Frame();
|
||||
var bp = ((IStylable)frame).GetProperty("padding-left");
|
||||
Assert.That(bp, Is.SameAs(PaddingElement.PaddingLeftProperty));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GetNonPublicProperty()
|
||||
{
|
||||
var label = new Label();
|
||||
var bp = ((IStylable)label).GetProperty("margin-right");
|
||||
Assert.That(bp, Is.SameAs(View.MarginRightProperty));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets.UnitTests
|
||||
{
|
||||
class MockStylable : IStyleSelectable
|
||||
{
|
||||
public IEnumerable<IStyleSelectable> Children { get; set; }
|
||||
public IList<string> Classes { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string[] NameAndBases { get; set; }
|
||||
public IStyleSelectable Parent { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class SelectorTests
|
||||
{
|
||||
IStyleSelectable Page;
|
||||
IStyleSelectable StackLayout => Page.Children.First();
|
||||
IStyleSelectable Label0 => StackLayout.Children.Skip(0).First();
|
||||
IStyleSelectable Label1 => StackLayout.Children.Skip(1).First();
|
||||
IStyleSelectable Label2 => ContentView0.Children.First();
|
||||
IStyleSelectable Label3 => StackLayout.Children.Skip(3).First();
|
||||
IStyleSelectable Label4 => StackLayout.Children.Skip(4).First();
|
||||
IStyleSelectable ContentView0 => StackLayout.Children.Skip(2).First();
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Page = new MockStylable {
|
||||
NameAndBases = new[] { "Page", "Layout", "VisualElement" },
|
||||
Children = new List<IStyleSelectable> {
|
||||
new MockStylable {
|
||||
NameAndBases = new[] { "StackLayout", "Layout", "VisualElement" },
|
||||
Children = new List<IStyleSelectable> {
|
||||
new MockStylable {NameAndBases = new[] { "Label", "VisualElement" }, Classes = new[]{"test"}}, //Label0
|
||||
new MockStylable {NameAndBases = new[] { "Label", "VisualElement" }}, //Label1
|
||||
new MockStylable { //ContentView0
|
||||
NameAndBases = new[] { "ContentView", "Layout", "VisualElement" },
|
||||
Classes = new[]{"test"},
|
||||
Children = new List<IStyleSelectable> {
|
||||
new MockStylable {NameAndBases = new[] { "Label", "VisualElement" }, Classes = new[]{"test"}}, //Label2
|
||||
}
|
||||
},
|
||||
new MockStylable {NameAndBases = new[] { "Label", "VisualElement" }, Id="foo"}, //Label3
|
||||
new MockStylable {NameAndBases = new[] { "Label", "VisualElement" }}, //Label4
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
SetParents(Page);
|
||||
}
|
||||
|
||||
void SetParents(IStyleSelectable stylable, IStyleSelectable parent = null)
|
||||
{
|
||||
((MockStylable)stylable).Parent = parent;
|
||||
if (stylable.Children == null)
|
||||
return;
|
||||
foreach (var s in stylable.Children)
|
||||
SetParents(s, stylable);
|
||||
}
|
||||
|
||||
[TestCase("label", true, true, true, true, true, false)]
|
||||
[TestCase(" label", true, true, true, true, true, false)]
|
||||
[TestCase("label ", true, true, true, true, true, false)]
|
||||
[TestCase(".test", true, false, true, false, false, true)]
|
||||
[TestCase("label.test", true, false, true, false, false, false)]
|
||||
[TestCase("stacklayout>label.test", true, false, false, false, false, false)]
|
||||
[TestCase("stacklayout >label.test", true, false, false, false, false, false)]
|
||||
[TestCase("stacklayout> label.test", true, false, false, false, false, false)]
|
||||
[TestCase("stacklayout label.test", true, false, true, false, false, false)]
|
||||
[TestCase("stacklayout label.test", true, false, true, false, false, false)]
|
||||
[TestCase("stacklayout .test", true, false, true, false, false, true)]
|
||||
[TestCase("stacklayout.test", false, false, false, false, false, false)]
|
||||
[TestCase("*", true, true, true, true, true, true)]
|
||||
[TestCase("#foo", false, false, false, true, false, false)]
|
||||
[TestCase("label#foo", false, false, false, true, false, false)]
|
||||
[TestCase("div#foo", false, false, false, false, false, false)]
|
||||
[TestCase(".test,#foo", true, false, true, true, false, true)]
|
||||
[TestCase(".test ,#foo", true, false, true, true, false, true)]
|
||||
[TestCase(".test, #foo", true, false, true, true, false, true)]
|
||||
[TestCase("#foo,.test", true, false, true, true, false, true)]
|
||||
[TestCase("#foo ,.test", true, false, true, true, false, true)]
|
||||
[TestCase("#foo, .test", true, false, true, true, false, true)]
|
||||
[TestCase("contentview+label", false, false, false, true, false, false)]
|
||||
[TestCase("contentview +label", false, false, false, true, false, false)]
|
||||
[TestCase("contentview+ label", false, false, false, true, false, false)]
|
||||
[TestCase("contentview~label", false, false, false, true, true, false)]
|
||||
[TestCase("contentview ~label", false, false, false, true, true, false)]
|
||||
[TestCase("contentview\r\n~label", false, false, false, true, true, false)]
|
||||
[TestCase("contentview~ label", false, false, false, true, true, false)]
|
||||
[TestCase("label~*", false, true, false, true, true, true)]
|
||||
[TestCase("label~.test", false, false, false, false, false, true)]
|
||||
[TestCase("label~#foo", false, false, false, true, false, false)]
|
||||
[TestCase("page contentview stacklayout label", false, false, false, false, false, false)]
|
||||
[TestCase("page stacklayout contentview label", false, false, true, false, false, false)]
|
||||
[TestCase("page contentview label", false, false, true, false, false, false)]
|
||||
[TestCase("page contentview>label", false, false, true, false, false, false)]
|
||||
[TestCase("page>stacklayout contentview label", false, false, true, false, false, false)]
|
||||
[TestCase("page stacklayout>contentview label", false, false, true, false, false, false)]
|
||||
[TestCase("page stacklayout contentview>label", false, false, true, false, false, false)]
|
||||
[TestCase("page>stacklayout>contentview label", false, false, true, false, false, false)]
|
||||
[TestCase("page>stack/* comment * */layout>contentview label", false, false, true, false, false, false)]
|
||||
[TestCase("page>stacklayout contentview>label", false, false, true, false, false, false)]
|
||||
[TestCase("page stacklayout>contentview>label", false, false, true, false, false, false)]
|
||||
[TestCase("page>stacklayout>contentview>label", false, false, true, false, false, false)]
|
||||
[TestCase("visualelement", false, false, false, false, false, false)]
|
||||
[TestCase("^visualelement", true, true, true, true, true, true)]
|
||||
[TestCase("^layout", false, false, false, false, false, true)]
|
||||
public void TestCase(string selectorString, bool label0match, bool label1match, bool label2match, bool label3match, bool label4match, bool content0match)
|
||||
{
|
||||
var selector = Selector.Parse(new CssReader(new StringReader(selectorString)));
|
||||
Assert.AreEqual(label0match, selector.Matches(Label0));
|
||||
Assert.AreEqual(label1match, selector.Matches(Label1));
|
||||
Assert.AreEqual(label2match, selector.Matches(Label2));
|
||||
Assert.AreEqual(label3match, selector.Matches(Label3));
|
||||
Assert.AreEqual(label4match, selector.Matches(Label4));
|
||||
Assert.AreEqual(content0match, selector.Matches(ContentView0));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
using Xamarin.Forms.Core.UnitTests;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class StyleTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Device.PlatformServices = new MockPlatformServices();
|
||||
Internals.Registrar.RegisterAll(new Type[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertiesAreApplied()
|
||||
{
|
||||
var styleString = @"background-color: #ff0000;";
|
||||
var style = Style.Parse(new CssReader(new StringReader(styleString)), '}');
|
||||
Assume.That(style, Is.Not.Null);
|
||||
|
||||
var ve = new VisualElement();
|
||||
Assume.That(ve.BackgroundColor, Is.EqualTo(Color.Default));
|
||||
style.Apply(ve);
|
||||
Assert.That(ve.BackgroundColor, Is.EqualTo(Color.Red));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertiesSetByStyleDoesNotOverrideManualOne()
|
||||
{
|
||||
var styleString = @"background-color: #ff0000;";
|
||||
var style = Style.Parse(new CssReader(new StringReader(styleString)), '}');
|
||||
Assume.That(style, Is.Not.Null);
|
||||
|
||||
var ve = new VisualElement() { BackgroundColor = Color.Pink };
|
||||
Assume.That(ve.BackgroundColor, Is.EqualTo(Color.Pink));
|
||||
|
||||
style.Apply(ve);
|
||||
Assert.That(ve.BackgroundColor, Is.EqualTo(Color.Pink));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StylesAreCascading()
|
||||
{
|
||||
var styleString = @"background-color: #ff0000; color: #00ff00;";
|
||||
var style = Style.Parse(new CssReader(new StringReader(styleString)), '}');
|
||||
Assume.That(style, Is.Not.Null);
|
||||
|
||||
var label = new Label();
|
||||
var layout = new StackLayout {
|
||||
Children = {
|
||||
label,
|
||||
}
|
||||
};
|
||||
|
||||
Assume.That(layout.BackgroundColor, Is.EqualTo(Color.Default));
|
||||
Assume.That(label.BackgroundColor, Is.EqualTo(Color.Default));
|
||||
Assume.That(label.TextColor, Is.EqualTo(Color.Default));
|
||||
|
||||
style.Apply(layout);
|
||||
Assert.That(layout.BackgroundColor, Is.EqualTo(Color.Red));
|
||||
Assert.That(label.BackgroundColor, Is.EqualTo(Color.Red));
|
||||
Assert.That(label.TextColor, Is.EqualTo(Color.Lime));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertiesAreOnlySetOnMatchingElements()
|
||||
{
|
||||
var styleString = @"background-color: #ff0000; color: #00ff00;";
|
||||
var style = Style.Parse(new CssReader(new StringReader(styleString)), '}');
|
||||
Assume.That(style, Is.Not.Null);
|
||||
|
||||
var layout = new StackLayout();
|
||||
Assert.That(layout.GetValue(TextElement.TextColorProperty), Is.EqualTo(Color.Default));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -180,6 +180,10 @@
|
|||
<Compile Include="NativeBindingTests.cs" />
|
||||
<Compile Include="TypedBindingUnitTests.cs" />
|
||||
<Compile Include="AnimationTests.cs" />
|
||||
<Compile Include="StyleSheets\MockStylable.cs" />
|
||||
<Compile Include="StyleSheets\SelectorTests.cs" />
|
||||
<Compile Include="StyleSheets\IStylableTest.cs" />
|
||||
<Compile Include="StyleSheets\StyleTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj">
|
||||
|
@ -212,4 +216,7 @@
|
|||
<LogicalName>Images/crimson.jpg</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<Folder Include="StyleSheets\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -34,9 +34,14 @@ namespace Xamarin.Forms
|
|||
|
||||
public event EventHandler BindingContextChanged;
|
||||
|
||||
internal void ClearValue(BindableProperty property, bool fromStyle)
|
||||
{
|
||||
ClearValue(property, fromStyle: fromStyle, checkAccess: true);
|
||||
}
|
||||
|
||||
public void ClearValue(BindableProperty property)
|
||||
{
|
||||
ClearValue(property, true);
|
||||
ClearValue(property, fromStyle: false, checkAccess: true);
|
||||
}
|
||||
|
||||
public void ClearValue(BindablePropertyKey propertyKey)
|
||||
|
@ -44,7 +49,7 @@ namespace Xamarin.Forms
|
|||
if (propertyKey == null)
|
||||
throw new ArgumentNullException("propertyKey");
|
||||
|
||||
ClearValue(propertyKey.BindableProperty, false);
|
||||
ClearValue(propertyKey.BindableProperty, fromStyle:false, checkAccess: false);
|
||||
}
|
||||
|
||||
public object GetValue(BindableProperty property)
|
||||
|
@ -442,18 +447,23 @@ namespace Xamarin.Forms
|
|||
bindable.OnBindingContextChanged();
|
||||
}
|
||||
|
||||
void ClearValue(BindableProperty property, bool checkaccess)
|
||||
void ClearValue(BindableProperty property, bool fromStyle, bool checkAccess)
|
||||
{
|
||||
if (property == null)
|
||||
throw new ArgumentNullException("property");
|
||||
throw new ArgumentNullException(nameof(property));
|
||||
|
||||
if (checkaccess && property.IsReadOnly)
|
||||
if (checkAccess && property.IsReadOnly)
|
||||
throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
|
||||
|
||||
BindablePropertyContext bpcontext = GetContext(property);
|
||||
if (bpcontext == null)
|
||||
return;
|
||||
|
||||
if ( fromStyle && bpcontext != null
|
||||
&& (bpcontext.Attributes & BindableContextAttributes.IsDefaultValue) != 0
|
||||
&& (bpcontext.Attributes & BindableContextAttributes.IsSetFromStyle) == 0)
|
||||
return;
|
||||
|
||||
object original = bpcontext.Value;
|
||||
|
||||
object newValue = property.GetDefaultValue(this);
|
||||
|
@ -461,8 +471,7 @@ namespace Xamarin.Forms
|
|||
bool same = Equals(original, newValue);
|
||||
if (!same)
|
||||
{
|
||||
if (property.PropertyChanging != null)
|
||||
property.PropertyChanging(this, original, newValue);
|
||||
property.PropertyChanging?.Invoke(this, original, newValue);
|
||||
|
||||
OnPropertyChanging(property.PropertyName);
|
||||
}
|
||||
|
@ -474,8 +483,7 @@ namespace Xamarin.Forms
|
|||
if (!same)
|
||||
{
|
||||
OnPropertyChanged(property.PropertyName);
|
||||
if (property.PropertyChanged != null)
|
||||
property.PropertyChanged(this, original, newValue);
|
||||
property.PropertyChanged?.Invoke(this, original, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
namespace Xamarin.Forms
|
||||
{
|
||||
static class BorderElement
|
||||
{
|
||||
public static readonly BindableProperty BorderColorProperty =
|
||||
BindableProperty.Create("BorderColor", typeof(Color), typeof(IBorderElement), Color.Default,
|
||||
propertyChanged: OnBorderColorPropertyChanged);
|
||||
|
||||
static void OnBorderColorPropertyChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
((IBorderElement)bindable).OnBorderColorPropertyChanged((Color)oldValue, (Color)newValue);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ using Xamarin.Forms.Platform;
|
|||
namespace Xamarin.Forms
|
||||
{
|
||||
[RenderWith(typeof(_ButtonRenderer))]
|
||||
public class Button : View, IFontElement, ITextElement, IButtonController, IElementConfiguration<Button>
|
||||
public class Button : View, IFontElement, ITextElement, IBorderElement, IButtonController, IElementConfiguration<Button>
|
||||
{
|
||||
public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(Button), null, propertyChanged: (bo, o, n) => ((Button)bo).OnCommandChanged());
|
||||
|
||||
|
@ -33,7 +33,7 @@ namespace Xamarin.Forms
|
|||
|
||||
public static readonly BindableProperty BorderWidthProperty = BindableProperty.Create("BorderWidth", typeof(double), typeof(Button), -1d);
|
||||
|
||||
public static readonly BindableProperty BorderColorProperty = BindableProperty.Create("BorderColor", typeof(Color), typeof(Button), Color.Default);
|
||||
public static readonly BindableProperty BorderColorProperty = BorderElement.BorderColorProperty;
|
||||
|
||||
public static readonly BindableProperty BorderRadiusProperty = BindableProperty.Create("BorderRadius", typeof(int), typeof(Button), 5);
|
||||
|
||||
|
@ -47,8 +47,8 @@ namespace Xamarin.Forms
|
|||
|
||||
public Color BorderColor
|
||||
{
|
||||
get { return (Color)GetValue(BorderColorProperty); }
|
||||
set { SetValue(BorderColorProperty, value); }
|
||||
get { return (Color)GetValue(BorderElement.BorderColorProperty); }
|
||||
set { SetValue(BorderElement.BorderColorProperty, value); }
|
||||
}
|
||||
|
||||
public int BorderRadius
|
||||
|
@ -245,6 +245,10 @@ namespace Xamarin.Forms
|
|||
{
|
||||
}
|
||||
|
||||
void IBorderElement.OnBorderColorPropertyChanged(Color oldValue, Color newValue)
|
||||
{
|
||||
}
|
||||
|
||||
[DebuggerDisplay("Image Position = {Position}, Spacing = {Spacing}")]
|
||||
[TypeConverter(typeof(ButtonContentTypeConverter))]
|
||||
public sealed class ButtonContentLayout
|
||||
|
|
|
@ -9,7 +9,7 @@ using Xamarin.Forms.Internals;
|
|||
|
||||
namespace Xamarin.Forms
|
||||
{
|
||||
public abstract class Element : BindableObject, IElement, INameScope, IElementController
|
||||
public abstract partial class Element : BindableObject, IElement, INameScope, IElementController
|
||||
{
|
||||
|
||||
public static readonly BindableProperty MenuProperty = BindableProperty.CreateAttached(nameof(Menu), typeof(Menu), typeof(Element), null);
|
||||
|
@ -428,7 +428,7 @@ namespace Xamarin.Forms
|
|||
}
|
||||
}
|
||||
|
||||
internal void OnParentResourcesChanged(object sender, ResourcesChangedEventArgs e)
|
||||
internal virtual void OnParentResourcesChanged(object sender, ResourcesChangedEventArgs e)
|
||||
{
|
||||
OnParentResourcesChanged(e.Values);
|
||||
}
|
||||
|
@ -447,7 +447,7 @@ namespace Xamarin.Forms
|
|||
base.OnRemoveDynamicResource(property);
|
||||
}
|
||||
|
||||
internal void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
|
||||
internal virtual void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
|
||||
{
|
||||
OnResourcesChanged(e.Values);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
using Xamarin.Forms.Internals;
|
||||
using Xamarin.Forms.StyleSheets;
|
||||
|
||||
namespace Xamarin.Forms
|
||||
{
|
||||
public partial class Element : IStyleSelectable
|
||||
{
|
||||
IEnumerable<IStyleSelectable> IStyleSelectable.Children => LogicalChildrenInternal;
|
||||
|
||||
IList<string> IStyleSelectable.Classes => null;
|
||||
|
||||
string IStyleSelectable.Id => StyleId;
|
||||
|
||||
string[] _styleSelectableNameAndBaseNames;
|
||||
string[] IStyleSelectable.NameAndBases {
|
||||
get {
|
||||
if (_styleSelectableNameAndBaseNames == null) {
|
||||
var list = new List<string>();
|
||||
var t = GetType();
|
||||
while (t != typeof(BindableObject)) {
|
||||
list.Add(t.Name);
|
||||
t = t.GetTypeInfo().BaseType;
|
||||
}
|
||||
_styleSelectableNameAndBaseNames = list.ToArray();
|
||||
}
|
||||
return _styleSelectableNameAndBaseNames;
|
||||
}
|
||||
}
|
||||
|
||||
IStyleSelectable IStyleSelectable.Parent => Parent;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,32 @@
|
|||
using System;
|
||||
|
||||
namespace Xamarin.Forms
|
||||
{
|
||||
[TypeConverter(typeof(FlowDirectionConverter))]
|
||||
public enum FlowDirection
|
||||
{
|
||||
MatchParent = 0,
|
||||
LeftToRight = 1,
|
||||
RightToLeft = 2,
|
||||
}
|
||||
|
||||
[Xaml.TypeConversion(typeof(FlowDirection))]
|
||||
public class FlowDirectionConverter : TypeConverter
|
||||
{
|
||||
public override object ConvertFromInvariantString(string value)
|
||||
{
|
||||
if (value != null) {
|
||||
if (Enum.TryParse(value, out FlowDirection direction))
|
||||
return direction;
|
||||
|
||||
if (value.Equals("ltr", StringComparison.OrdinalIgnoreCase))
|
||||
return FlowDirection.LeftToRight;
|
||||
if (value.Equals("rtl", StringComparison.OrdinalIgnoreCase))
|
||||
return FlowDirection.RightToLeft;
|
||||
if (value.Equals("inherit", StringComparison.OrdinalIgnoreCase))
|
||||
return FlowDirection.MatchParent;
|
||||
}
|
||||
throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(FlowDirection)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,8 +20,9 @@ namespace Xamarin.Forms
|
|||
double size;
|
||||
if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out size))
|
||||
return size;
|
||||
var ignoreCase = (serviceProvider?.GetService(typeof(IConverterOptions)) as IConverterOptions)?.IgnoreCase ?? false;
|
||||
NamedSize namedSize;
|
||||
if (Enum.TryParse(value, out namedSize))
|
||||
if (Enum.TryParse(value, ignoreCase, out namedSize))
|
||||
{
|
||||
Type type;
|
||||
var valueTargetProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
|
||||
|
|
|
@ -5,9 +5,12 @@ namespace Xamarin.Forms
|
|||
{
|
||||
[ContentProperty("Content")]
|
||||
[RenderWith(typeof(_FrameRenderer))]
|
||||
public class Frame : ContentView, IElementConfiguration<Frame>, IPaddingElement
|
||||
public class Frame : ContentView, IElementConfiguration<Frame>, IPaddingElement, IBorderElement
|
||||
{
|
||||
public static readonly BindableProperty OutlineColorProperty = BindableProperty.Create("OutlineColor", typeof(Color), typeof(Frame), Color.Default);
|
||||
[Obsolete("OutlineColorProperty is obsolete as of version 3.0.0. Please use BorderColorProperty instead.")]
|
||||
public static readonly BindableProperty OutlineColorProperty = BorderElement.BorderColorProperty;
|
||||
|
||||
public static readonly BindableProperty BorderColorProperty = BorderElement.BorderColorProperty;
|
||||
|
||||
public static readonly BindableProperty HasShadowProperty = BindableProperty.Create("HasShadow", typeof(bool), typeof(Frame), true);
|
||||
|
||||
|
@ -32,12 +35,19 @@ namespace Xamarin.Forms
|
|||
set { SetValue(HasShadowProperty, value); }
|
||||
}
|
||||
|
||||
[Obsolete("OutlineColor is obsolete as of version 3.0.0. Please use BorderColor instead.")]
|
||||
public Color OutlineColor
|
||||
{
|
||||
get { return (Color)GetValue(OutlineColorProperty); }
|
||||
set { SetValue(OutlineColorProperty, value); }
|
||||
}
|
||||
|
||||
public Color BorderColor
|
||||
{
|
||||
get { return (Color)GetValue(BorderElement.BorderColorProperty); }
|
||||
set { SetValue(BorderElement.BorderColorProperty, value); }
|
||||
}
|
||||
|
||||
public float CornerRadius
|
||||
{
|
||||
get { return (float)GetValue(CornerRadiusProperty); }
|
||||
|
@ -48,5 +58,12 @@ namespace Xamarin.Forms
|
|||
{
|
||||
return _platformConfigurationRegistry.Value.On<T>();
|
||||
}
|
||||
|
||||
void IBorderElement.OnBorderColorPropertyChanged(Color oldValue, Color newValue)
|
||||
{
|
||||
#pragma warning disable 0618 // retain until OutlineColor removed
|
||||
OnPropertyChanged(nameof(OutlineColor));
|
||||
#pragma warning restore
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace Xamarin.Forms
|
||||
{
|
||||
interface IBorderElement
|
||||
{
|
||||
//note to implementor: implement this property publicly
|
||||
Color BorderColor { get; }
|
||||
|
||||
//note to implementor: but implement the methods explicitly
|
||||
void OnBorderColorPropertyChanged(Color oldValue, Color newValue);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace Xamarin.Forms.Xaml
|
||||
{
|
||||
interface IConverterOptions
|
||||
{
|
||||
bool IgnoreCase { get; }
|
||||
}
|
||||
}
|
|
@ -5,14 +5,14 @@ namespace Xamarin.Forms.Internals
|
|||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public interface IFontElement
|
||||
{
|
||||
//note to implementor: implement the properties publicly
|
||||
//note to implementor: implement these properties publicly
|
||||
FontAttributes FontAttributes { get; }
|
||||
string FontFamily { get; }
|
||||
|
||||
[TypeConverter(typeof(FontSizeConverter))]
|
||||
double FontSize { get; }
|
||||
|
||||
//note to implementor: but implement the methods explicitly
|
||||
//note to implementor: but implement these methods explicitly
|
||||
void OnFontFamilyChanged(string oldValue, string newValue);
|
||||
void OnFontSizeChanged(double oldValue, double newValue);
|
||||
double FontSizeDefaultValueCreator();
|
||||
|
|
|
@ -2,7 +2,7 @@ using System;
|
|||
|
||||
namespace Xamarin.Forms
|
||||
{
|
||||
internal interface IStyle
|
||||
interface IStyle
|
||||
{
|
||||
Type TargetType { get; }
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@ namespace Xamarin.Forms
|
|||
{
|
||||
interface ITextElement
|
||||
{
|
||||
//note to implementor: implement the properties publicly
|
||||
//note to implementor: implement this property publicly
|
||||
Color TextColor { get; }
|
||||
|
||||
//note to implementor: but implement the methods explicitly
|
||||
//note to implementor: but implement this method explicitly
|
||||
void OnTextColorPropertyChanged(Color oldValue, Color newValue);
|
||||
}
|
||||
}
|
|
@ -467,4 +467,4 @@ namespace Xamarin.Forms
|
|||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Internals;
|
||||
using Xamarin.Forms.StyleSheets;
|
||||
|
||||
[assembly: InternalsVisibleTo("iOSUnitTests")]
|
||||
[assembly: InternalsVisibleTo("Xamarin.Forms.Controls")]
|
||||
|
@ -30,4 +32,31 @@ using Xamarin.Forms.Internals;
|
|||
[assembly: InternalsVisibleTo("Xamarin.Forms.CarouselView")]
|
||||
[assembly: Preserve]
|
||||
|
||||
[assembly: XmlnsDefinition("http://xamarin.com/schemas/2014/forms", "Xamarin.Forms")]
|
||||
[assembly: XmlnsDefinition("http://xamarin.com/schemas/2014/forms", "Xamarin.Forms")]
|
||||
|
||||
[assembly: StyleProperty("background-color", typeof(VisualElement), nameof(VisualElement.BackgroundColorProperty))]
|
||||
[assembly: StyleProperty("background-image", typeof(Page), nameof(Page.BackgroundImageProperty))]
|
||||
[assembly: StyleProperty("border-color", typeof(Button), nameof(BorderElement.BorderColorProperty))]
|
||||
[assembly: StyleProperty("border-width", typeof(Button), nameof(Button.BorderWidthProperty))]
|
||||
[assembly: StyleProperty("color", typeof(ITextElement), nameof(TextElement.TextColorProperty))]
|
||||
[assembly: StyleProperty("direction", typeof(VisualElement), nameof(VisualElement.FlowDirectionProperty))]
|
||||
[assembly: StyleProperty("font-family", typeof(IFontElement), nameof(FontElement.FontFamilyProperty))]
|
||||
[assembly: StyleProperty("font-size", typeof(IFontElement), nameof(FontElement.FontSizeProperty))]
|
||||
[assembly: StyleProperty("font-style", typeof(IFontElement), nameof(FontElement.FontAttributesProperty))]
|
||||
[assembly: StyleProperty("height", typeof(VisualElement), nameof(VisualElement.HeightRequestProperty))]
|
||||
[assembly: StyleProperty("margin", typeof(View), nameof(View.MarginProperty))]
|
||||
[assembly: StyleProperty("margin-left", typeof(View), nameof(View.MarginLeftProperty))]
|
||||
[assembly: StyleProperty("margin-top", typeof(View), nameof(View.MarginTopProperty))]
|
||||
[assembly: StyleProperty("margin-right", typeof(View), nameof(View.MarginRightProperty))]
|
||||
[assembly: StyleProperty("margin-bottom", typeof(View), nameof(View.MarginBottomProperty))]
|
||||
[assembly: StyleProperty("min-height", typeof(VisualElement), nameof(VisualElement.MinimumHeightRequestProperty))]
|
||||
[assembly: StyleProperty("min-width", typeof(VisualElement), nameof(VisualElement.MinimumWidthRequestProperty))]
|
||||
[assembly: StyleProperty("opacity", typeof(VisualElement), nameof(VisualElement.OpacityProperty))]
|
||||
[assembly: StyleProperty("padding", typeof(IPaddingElement), nameof(PaddingElement.PaddingProperty))]
|
||||
[assembly: StyleProperty("padding-left", typeof(IPaddingElement), nameof(PaddingElement.PaddingLeftProperty), PropertyOwnerType=typeof(PaddingElement))]
|
||||
[assembly: StyleProperty("padding-top", typeof(IPaddingElement), nameof(PaddingElement.PaddingTopProperty), PropertyOwnerType = typeof(PaddingElement))]
|
||||
[assembly: StyleProperty("padding-right", typeof(IPaddingElement), nameof(PaddingElement.PaddingRightProperty), PropertyOwnerType = typeof(PaddingElement))]
|
||||
[assembly: StyleProperty("padding-bottom", typeof(IPaddingElement), nameof(PaddingElement.PaddingBottomProperty), PropertyOwnerType = typeof(PaddingElement))]
|
||||
[assembly: StyleProperty("text-align", typeof(ITextAlignmentElement), nameof(TextAlignmentElement.HorizontalTextAlignmentProperty))]
|
||||
[assembly: StyleProperty("visibility", typeof(VisualElement), nameof(VisualElement.IsVisibleProperty))]
|
||||
[assembly: StyleProperty("width", typeof(VisualElement), nameof(VisualElement.WidthRequestProperty))]
|
|
@ -97,14 +97,14 @@ namespace Xamarin.Forms.Internals
|
|||
|
||||
type = attribute.Type;
|
||||
|
||||
if (type.Name.StartsWith("_"))
|
||||
if (type.Name.StartsWith("_", StringComparison.Ordinal))
|
||||
{
|
||||
// TODO: Remove attribute2 once renderer names have been unified across all platforms
|
||||
var attribute2 = type.GetTypeInfo().GetCustomAttribute<RenderWithAttribute>();
|
||||
if (attribute2 != null)
|
||||
type = attribute2.Type;
|
||||
|
||||
if (type.Name.StartsWith("_"))
|
||||
if (type.Name.StartsWith("_", StringComparison.Ordinal))
|
||||
{
|
||||
Register(viewType, null); // Cache this result so we don't work through this chain again
|
||||
return null;
|
||||
|
@ -156,6 +156,7 @@ namespace Xamarin.Forms.Internals
|
|||
}
|
||||
|
||||
internal static Dictionary<string, Type> Effects { get; } = new Dictionary<string, Type>();
|
||||
internal static Dictionary<string, StyleSheets.StylePropertyAttribute> StyleProperties { get; } = new Dictionary<string, StyleSheets.StylePropertyAttribute>();
|
||||
|
||||
public static IEnumerable<Assembly> ExtraAssemblies { get; set; }
|
||||
|
||||
|
@ -165,9 +166,7 @@ namespace Xamarin.Forms.Internals
|
|||
{
|
||||
Assembly[] assemblies = Device.GetAssemblies();
|
||||
if (ExtraAssemblies != null)
|
||||
{
|
||||
assemblies = assemblies.Union(ExtraAssemblies).ToArray();
|
||||
}
|
||||
|
||||
Assembly defaultRendererAssembly = Device.PlatformServices.GetType().GetTypeInfo().Assembly;
|
||||
int indexOfExecuting = Array.IndexOf(assemblies, defaultRendererAssembly);
|
||||
|
@ -185,36 +184,38 @@ namespace Xamarin.Forms.Internals
|
|||
foreach (Type attrType in attrTypes)
|
||||
{
|
||||
Attribute[] attributes = assembly.GetCustomAttributes(attrType).ToArray();
|
||||
if (attributes.Length == 0)
|
||||
continue;
|
||||
|
||||
foreach (HandlerAttribute attribute in attributes)
|
||||
var length = attributes.Length;
|
||||
for (var i = 0; i < length;i++)
|
||||
{
|
||||
var attribute = (HandlerAttribute)attributes[i];
|
||||
if (attribute.ShouldRegister())
|
||||
Registered.Register(attribute.HandlerType, attribute.TargetType);
|
||||
}
|
||||
}
|
||||
|
||||
string resolutionName = assembly.FullName;
|
||||
var resolutionNameAttribute = (ResolutionGroupNameAttribute)assembly.GetCustomAttribute(typeof(ResolutionGroupNameAttribute));
|
||||
if (resolutionNameAttribute != null)
|
||||
resolutionName = resolutionNameAttribute.ShortName;
|
||||
|
||||
Attribute[] effectAttributes = assembly.GetCustomAttributes(typeof(ExportEffectAttribute)).ToArray();
|
||||
if (effectAttributes.Length > 0)
|
||||
var exportEffectsLength = effectAttributes.Length;
|
||||
for (var i = 0; i < exportEffectsLength;i++)
|
||||
{
|
||||
var resolutionNameAttribute = (ResolutionGroupNameAttribute)assembly.GetCustomAttribute(typeof(ResolutionGroupNameAttribute));
|
||||
if (resolutionNameAttribute != null)
|
||||
{
|
||||
resolutionName = resolutionNameAttribute.ShortName;
|
||||
}
|
||||
var effect = (ExportEffectAttribute)effectAttributes[i];
|
||||
Effects [resolutionName + "." + effect.Id] = effect.Type;
|
||||
}
|
||||
|
||||
foreach (Attribute attribute in effectAttributes)
|
||||
{
|
||||
var effect = (ExportEffectAttribute)attribute;
|
||||
Effects[resolutionName + "." + effect.Id] = effect.Type;
|
||||
}
|
||||
Attribute[] styleAttributes = assembly.GetCustomAttributes(typeof(StyleSheets.StylePropertyAttribute)).ToArray();
|
||||
var stylePropertiesLength = styleAttributes.Length;
|
||||
for (var i = 0; i < stylePropertiesLength; i++)
|
||||
{
|
||||
var attribute = (StyleSheets.StylePropertyAttribute)styleAttributes[i];
|
||||
StyleProperties[attribute.CssPropertyName] = attribute;
|
||||
}
|
||||
}
|
||||
|
||||
DependencyService.Initialize(assemblies);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace Xamarin.Forms
|
|||
if (type != null)
|
||||
_mergedInstance = s_instances.GetValue(type, (key) => (ResourceDictionary)Activator.CreateInstance(key));
|
||||
else
|
||||
_mergedInstance = DependencyService.Get<IResourcesLoader>().CreateResourceDictionary(resourcePath, assembly, lineInfo);
|
||||
_mergedInstance = DependencyService.Get<IResourcesLoader>().CreateFromResource<ResourceDictionary>(resourcePath, assembly, lineInfo);
|
||||
OnValuesChanged(_mergedInstance.ToArray());
|
||||
}
|
||||
|
||||
|
@ -84,6 +84,16 @@ namespace Xamarin.Forms
|
|||
}
|
||||
}
|
||||
|
||||
internal IList<StyleSheets.StyleSheet> StyleSheets { get; set; }
|
||||
|
||||
void StyleSheetsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
switch (e.Action) {
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
ValuesChanged?.Invoke(this, ResourcesChangedEventArgs.StyleSheets);
|
||||
break;
|
||||
}
|
||||
}
|
||||
IList<ResourceDictionary> _collectionTrack;
|
||||
|
||||
void MergedDictionaries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
|
@ -284,6 +294,13 @@ namespace Xamarin.Forms
|
|||
MergedDictionaries.Add(mergedResourceDictionary);
|
||||
}
|
||||
|
||||
public void Add(StyleSheets.StyleSheet styleSheet)
|
||||
{
|
||||
StyleSheets = StyleSheets ?? new List<StyleSheets.StyleSheet>(2);
|
||||
StyleSheets.Add(styleSheet);
|
||||
ValuesChanged?.Invoke(this, ResourcesChangedEventArgs.StyleSheets);
|
||||
}
|
||||
|
||||
void OnValueChanged(string key, object value)
|
||||
{
|
||||
OnValuesChanged(new KeyValuePair<string, object>(key, value));
|
||||
|
|
|
@ -7,6 +7,8 @@ namespace Xamarin.Forms.Internals
|
|||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class ResourcesChangedEventArgs : EventArgs
|
||||
{
|
||||
public static readonly ResourcesChangedEventArgs StyleSheets = new ResourcesChangedEventArgs(null);
|
||||
|
||||
public ResourcesChangedEventArgs(IEnumerable<KeyValuePair<string, object>> values)
|
||||
{
|
||||
Values = values;
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets
|
||||
{
|
||||
static class CharExtensions
|
||||
{
|
||||
//w [ \t\r\n\f]*
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsW(this char c)
|
||||
{
|
||||
return c == ' '
|
||||
|| c == '\t'
|
||||
|| c == '\r'
|
||||
|| c == '\n'
|
||||
|| c == '\f';
|
||||
}
|
||||
|
||||
//nmstart [_a-z]|{nonascii}|{escape}
|
||||
//escape {unicode}|\\[^\n\r\f0-9a-f]
|
||||
//nonascii [^\0-\237]
|
||||
// TODO support escape and nonascii
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsNmStart(this char c)
|
||||
{
|
||||
return c == '_' || char.IsLetter(c);
|
||||
}
|
||||
|
||||
//nmchar [_a-z0-9-]|{nonascii}|{escape}
|
||||
//unicode \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
|
||||
//escape {unicode}|\\[^\n\r\f0-9a-f]
|
||||
//nonascii [^\0-\237]
|
||||
//TODO support escape, nonascii and unicode
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsNmChar(this char c)
|
||||
{
|
||||
return c == '_'
|
||||
|| c == '-'
|
||||
|| char.IsLetterOrDigit(c);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets
|
||||
{
|
||||
sealed class CssReader : TextReader
|
||||
{
|
||||
readonly TextReader _reader;
|
||||
|
||||
public CssReader(TextReader reader)
|
||||
{
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException(nameof(reader));
|
||||
|
||||
_reader = reader;
|
||||
}
|
||||
|
||||
readonly Queue<char> _cache = new Queue<char>();
|
||||
|
||||
//skip comments
|
||||
//TODO unescape escaped sequences
|
||||
public override int Peek()
|
||||
{
|
||||
if (_cache.Count > 0)
|
||||
return _cache.Peek();
|
||||
|
||||
int p = _reader.Peek();
|
||||
if (p <= 0)
|
||||
return p;
|
||||
if (unchecked((char)p) != '/')
|
||||
return p;
|
||||
|
||||
_cache.Enqueue(unchecked((char)_reader.Read()));
|
||||
p = _reader.Peek();
|
||||
if (p <= 0)
|
||||
return _cache.Peek();
|
||||
if (unchecked((char)p) != '*')
|
||||
return _cache.Peek();
|
||||
|
||||
_cache.Clear();
|
||||
_reader.Read(); //consume the '*'
|
||||
|
||||
bool hasStar = false;
|
||||
while (true) {
|
||||
var next = _reader.Read();
|
||||
if (next <= 0)
|
||||
return next;
|
||||
if (unchecked((char)next) == '*')
|
||||
hasStar = true;
|
||||
else if (hasStar && unchecked((char)next) == '/')
|
||||
return Peek(); //recursively call self for comments following comments
|
||||
else
|
||||
hasStar = false;
|
||||
}
|
||||
}
|
||||
|
||||
//skip comments
|
||||
//TODO unescape escaped sequences
|
||||
public override int Read()
|
||||
{
|
||||
if (_cache.Count > 0)
|
||||
return _cache.Dequeue();
|
||||
|
||||
int p = _reader.Read();
|
||||
if (p <= 0)
|
||||
return p;
|
||||
var c = unchecked((char)p);
|
||||
if (c != '/')
|
||||
return p;
|
||||
|
||||
_cache.Enqueue(c);
|
||||
p = _reader.Read();
|
||||
if (p <= 0)
|
||||
return _cache.Dequeue();
|
||||
c = unchecked((char)p);
|
||||
if (c != '*')
|
||||
return _cache.Dequeue();
|
||||
|
||||
_cache.Clear();
|
||||
_reader.Read(); //consume the '*'
|
||||
|
||||
bool hasStar = false;
|
||||
while (true) {
|
||||
var next = _reader.Read();
|
||||
if (next <= 0)
|
||||
return next;
|
||||
if (unchecked((char)next) == '*')
|
||||
hasStar = true;
|
||||
else if (hasStar && unchecked((char)next) == '/')
|
||||
return Read(); //recursively call self for comments following comments
|
||||
else
|
||||
hasStar = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets
|
||||
{
|
||||
interface IStyleSelectable
|
||||
{
|
||||
string[] NameAndBases { get; }
|
||||
string Id { get; }
|
||||
IStyleSelectable Parent { get; }
|
||||
IList<string> Classes { get; }
|
||||
IEnumerable<IStyleSelectable> Children { get; }
|
||||
}
|
||||
|
||||
interface IStylable
|
||||
{
|
||||
BindableProperty GetProperty(string key);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
using System;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets
|
||||
{
|
||||
abstract class Selector
|
||||
{
|
||||
Selector()
|
||||
{
|
||||
}
|
||||
|
||||
public static Selector Parse(CssReader reader, char stopChar = '\0')
|
||||
{
|
||||
Selector root = All, workingRoot = All;
|
||||
Operator workingRootParent = null;
|
||||
Action<Operator, Selector> setCurrentSelector = (op, sel) => SetCurrentSelector(ref root, ref workingRoot, ref workingRootParent, op, sel);
|
||||
|
||||
int p;
|
||||
reader.SkipWhiteSpaces();
|
||||
while ((p = reader.Peek()) > 0) {
|
||||
switch (unchecked((char)p)) {
|
||||
case '*':
|
||||
setCurrentSelector(new And(), All);
|
||||
reader.Read();
|
||||
break;
|
||||
case '.':
|
||||
reader.Read();
|
||||
var className = reader.ReadIdent();
|
||||
if (className == null)
|
||||
return Invalid;
|
||||
setCurrentSelector(new And(), new Class(className));
|
||||
break;
|
||||
case '#':
|
||||
reader.Read();
|
||||
var id = reader.ReadName();
|
||||
if (id == null)
|
||||
return Invalid;
|
||||
setCurrentSelector(new And(), new Id(id));
|
||||
break;
|
||||
case '[':
|
||||
throw new NotImplementedException("Attributes not implemented");
|
||||
case ',':
|
||||
reader.Read();
|
||||
setCurrentSelector(new Or(), All);
|
||||
reader.SkipWhiteSpaces();
|
||||
break;
|
||||
case '+':
|
||||
reader.Read();
|
||||
setCurrentSelector(new Adjacent(), All);
|
||||
reader.SkipWhiteSpaces();
|
||||
break;
|
||||
case '~':
|
||||
reader.Read();
|
||||
setCurrentSelector(new Sibling(), All);
|
||||
reader.SkipWhiteSpaces();
|
||||
break;
|
||||
case '>':
|
||||
reader.Read();
|
||||
setCurrentSelector(new Child(), All);
|
||||
reader.SkipWhiteSpaces();
|
||||
break;
|
||||
case '^': //not in CSS spec
|
||||
reader.Read();
|
||||
var element = reader.ReadIdent();
|
||||
if (element == null)
|
||||
return Invalid;
|
||||
setCurrentSelector(new And(), new Base(element));
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\f':
|
||||
reader.Read();
|
||||
bool processWs = false;
|
||||
while ((p = reader.Peek()) > 0) {
|
||||
var c = unchecked((char)p);
|
||||
if (char.IsWhiteSpace(c)) {
|
||||
reader.Read();
|
||||
continue;
|
||||
}
|
||||
processWs = (c != '+'
|
||||
&& c != '>'
|
||||
&& c != ','
|
||||
&& c != '~'
|
||||
&& c != '^'
|
||||
&& c != stopChar);
|
||||
break;
|
||||
}
|
||||
if (!processWs)
|
||||
break;
|
||||
setCurrentSelector(new Descendent(), All);
|
||||
reader.SkipWhiteSpaces();
|
||||
break;
|
||||
default:
|
||||
if (unchecked((char)p) == stopChar)
|
||||
return root;
|
||||
|
||||
var elementName = reader.ReadIdent();
|
||||
if (elementName == null)
|
||||
return Invalid;
|
||||
setCurrentSelector(new And(), new Element(elementName));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
static void SetCurrentSelector(ref Selector root, ref Selector workingRoot, ref Operator workingRootParent, Operator op, Selector sel)
|
||||
{
|
||||
var updateRoot = root == workingRoot;
|
||||
|
||||
op.Left = workingRoot;
|
||||
op.Right = sel;
|
||||
workingRoot = op;
|
||||
if (workingRootParent != null)
|
||||
workingRootParent.Right = workingRoot;
|
||||
|
||||
if (updateRoot)
|
||||
root = workingRoot;
|
||||
|
||||
if (workingRoot is Or) {
|
||||
workingRootParent = (Operator)workingRoot;
|
||||
workingRoot = sel;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract bool Matches(IStyleSelectable styleable);
|
||||
|
||||
internal static Selector Invalid = new Generic(s => false);
|
||||
internal static Selector All = new Generic(s => true);
|
||||
|
||||
abstract class UnarySelector : Selector
|
||||
{
|
||||
}
|
||||
|
||||
abstract class Operator : Selector
|
||||
{
|
||||
public Selector Left { get; set; } = Invalid;
|
||||
public Selector Right { get; set; } = Invalid;
|
||||
}
|
||||
|
||||
sealed class Generic : UnarySelector
|
||||
{
|
||||
readonly Func<IStyleSelectable, bool> func;
|
||||
public Generic(Func<IStyleSelectable, bool> func)
|
||||
{
|
||||
this.func = func;
|
||||
}
|
||||
|
||||
public override bool Matches(IStyleSelectable styleable) => func(styleable);
|
||||
}
|
||||
|
||||
sealed class Class : UnarySelector
|
||||
{
|
||||
public Class(string className)
|
||||
{
|
||||
ClassName = className;
|
||||
}
|
||||
|
||||
public string ClassName { get; }
|
||||
public override bool Matches(IStyleSelectable styleable)
|
||||
=> styleable.Classes != null && styleable.Classes.Contains(ClassName);
|
||||
}
|
||||
|
||||
sealed class Id : UnarySelector
|
||||
{
|
||||
public Id(string id)
|
||||
{
|
||||
IdName = id;
|
||||
}
|
||||
|
||||
public string IdName { get; }
|
||||
public override bool Matches(IStyleSelectable styleable) => styleable.Id == IdName;
|
||||
}
|
||||
|
||||
sealed class Or : Operator
|
||||
{
|
||||
public override bool Matches(IStyleSelectable styleable) => Right.Matches(styleable) || Left.Matches(styleable);
|
||||
}
|
||||
|
||||
sealed class And : Operator
|
||||
{
|
||||
public override bool Matches(IStyleSelectable styleable) => Right.Matches(styleable) && Left.Matches(styleable);
|
||||
}
|
||||
|
||||
sealed class Element : UnarySelector
|
||||
{
|
||||
public Element(string elementName)
|
||||
{
|
||||
ElementName = elementName;
|
||||
}
|
||||
|
||||
public string ElementName { get; }
|
||||
public override bool Matches(IStyleSelectable styleable) =>
|
||||
string.Equals(styleable.NameAndBases[0], ElementName, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
sealed class Base : UnarySelector
|
||||
{
|
||||
public Base(string elementName)
|
||||
{
|
||||
ElementName = elementName;
|
||||
}
|
||||
|
||||
public string ElementName { get; }
|
||||
public override bool Matches(IStyleSelectable styleable) {
|
||||
for (var i = 0; i < styleable.NameAndBases.Length; i++)
|
||||
if (string.Equals(styleable.NameAndBases[i], ElementName, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Child : Operator
|
||||
{
|
||||
public override bool Matches(IStyleSelectable styleable) =>
|
||||
Right.Matches(styleable) && styleable.Parent != null && Left.Matches(styleable.Parent);
|
||||
}
|
||||
|
||||
sealed class Descendent : Operator
|
||||
{
|
||||
public override bool Matches(IStyleSelectable styleable)
|
||||
{
|
||||
if (!Right.Matches(styleable))
|
||||
return false;
|
||||
var parent = styleable.Parent;
|
||||
while (parent != null) {
|
||||
if (Left.Matches(parent))
|
||||
return true;
|
||||
parent = parent.Parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Adjacent : Operator
|
||||
{
|
||||
public override bool Matches(IStyleSelectable styleable)
|
||||
{
|
||||
if (!Right.Matches(styleable))
|
||||
return false;
|
||||
if (styleable.Parent == null)
|
||||
return false;
|
||||
|
||||
IStyleSelectable prev = null;
|
||||
foreach (var elem in styleable.Parent.Children) {
|
||||
if (elem == styleable && prev != null)
|
||||
return Left.Matches(prev);
|
||||
prev = elem;
|
||||
}
|
||||
return false;
|
||||
//var index = styleable.Parent.Children.IndexOf(styleable);
|
||||
//if (index == 0)
|
||||
// return false;
|
||||
//var adjacent = styleable.Parent.Children[index - 1];
|
||||
//return Left.Matches(adjacent);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Sibling : Operator
|
||||
{
|
||||
public override bool Matches(IStyleSelectable styleable)
|
||||
{
|
||||
if (!Right.Matches(styleable))
|
||||
return false;
|
||||
if (styleable.Parent == null)
|
||||
return false;
|
||||
|
||||
int selfIndex = 0;
|
||||
bool foundSelfInParent = false;
|
||||
foreach (var elem in styleable.Parent.Children) {
|
||||
if (elem == styleable) {
|
||||
foundSelfInParent = true;
|
||||
break;
|
||||
}
|
||||
++selfIndex;
|
||||
}
|
||||
|
||||
if (!foundSelfInParent)
|
||||
return false;
|
||||
|
||||
int index = 0;
|
||||
foreach (var elem in styleable.Parent.Children) {
|
||||
if (index >= selfIndex)
|
||||
return false;
|
||||
if (Left.Matches(elem))
|
||||
return true;
|
||||
++index;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
//var index = styleable.Parent.Children.IndexOf(styleable);
|
||||
//if (index == 0)
|
||||
// return false;
|
||||
//int siblingIndex = -1;
|
||||
//for (var i = 0; i < index; i++)
|
||||
// if (Left.Matches(styleable.Parent.Children[i])) {
|
||||
// siblingIndex = i;
|
||||
// break;
|
||||
// }
|
||||
//return siblingIndex != -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets
|
||||
{
|
||||
sealed class Style
|
||||
{
|
||||
Style()
|
||||
{
|
||||
}
|
||||
|
||||
public IDictionary<string, string> Declarations { get; set; } = new Dictionary<string, string>();
|
||||
Dictionary<KeyValuePair<string, string>, object> convertedValues = new Dictionary<KeyValuePair<string, string>, object>();
|
||||
|
||||
public static Style Parse(CssReader reader, char stopChar = '\0')
|
||||
{
|
||||
Style style = new Style();
|
||||
string propertyName = null, propertyValue = null;
|
||||
|
||||
int p;
|
||||
reader.SkipWhiteSpaces();
|
||||
bool readingName = true;
|
||||
while ((p = reader.Peek()) > 0) {
|
||||
switch (unchecked((char)p)) {
|
||||
case ':':
|
||||
reader.Read();
|
||||
readingName = false;
|
||||
reader.SkipWhiteSpaces();
|
||||
break;
|
||||
case ';':
|
||||
reader.Read();
|
||||
if (!string.IsNullOrEmpty(propertyName) && !string.IsNullOrEmpty(propertyValue))
|
||||
style.Declarations.Add(propertyName, propertyValue);
|
||||
propertyName = propertyValue = null;
|
||||
readingName = true;
|
||||
reader.SkipWhiteSpaces();
|
||||
break;
|
||||
default:
|
||||
if ((char)p == stopChar)
|
||||
return style;
|
||||
|
||||
if (readingName) {
|
||||
propertyName = reader.ReadIdent();
|
||||
if (propertyName == null)
|
||||
throw new Exception();
|
||||
} else
|
||||
propertyValue = reader.ReadUntil(stopChar, ';', ':');
|
||||
break;
|
||||
}
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
public void Apply(VisualElement styleable)
|
||||
{
|
||||
if (styleable == null)
|
||||
throw new ArgumentNullException(nameof(styleable));
|
||||
|
||||
foreach (var decl in Declarations) {
|
||||
var property = ((IStylable)styleable).GetProperty(decl.Key);
|
||||
if (property == null)
|
||||
continue;
|
||||
if (string.Equals(decl.Value, "initial", StringComparison.OrdinalIgnoreCase))
|
||||
styleable.ClearValue(property, fromStyle: true);
|
||||
else {
|
||||
object value;
|
||||
if (!convertedValues.TryGetValue(decl, out value))
|
||||
convertedValues[decl] = (value = Convert(styleable, decl.Value, property));
|
||||
styleable.SetValue(property, value, fromStyle: true);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var child in styleable.LogicalChildrenInternal) {
|
||||
var ve = child as VisualElement;
|
||||
if (ve == null)
|
||||
continue;
|
||||
Apply(ve);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static object Convert(object target, object value, BindableProperty property)
|
||||
{
|
||||
Func<MemberInfo> minforetriever = () => property.DeclaringType.GetRuntimeProperty(property.PropertyName) as MemberInfo
|
||||
?? property.DeclaringType.GetRuntimeMethod("Get" + property.PropertyName, new[] { typeof(BindableObject) }) as MemberInfo;
|
||||
var serviceProvider = new StyleSheetServiceProvider(target, property);
|
||||
return value.ConvertTo(property.ReturnType, minforetriever, serviceProvider);
|
||||
}
|
||||
|
||||
public void UnApply(IStylable styleable)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = true)]
|
||||
sealed class StylePropertyAttribute : Attribute
|
||||
{
|
||||
public string CssPropertyName { get; }
|
||||
public string BindablePropertyName { get; }
|
||||
public Type TargetType { get; }
|
||||
public Type PropertyOwnerType { get; set; }
|
||||
public BindableProperty BindableProperty { get; set; }
|
||||
|
||||
|
||||
public StylePropertyAttribute(string cssPropertyName, Type targetType, string bindablePropertyName)
|
||||
{
|
||||
CssPropertyName = cssPropertyName;
|
||||
BindablePropertyName = bindablePropertyName;
|
||||
TargetType = targetType;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Xml;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets
|
||||
{
|
||||
public sealed class StyleSheet : IStyle
|
||||
{
|
||||
StyleSheet()
|
||||
{
|
||||
}
|
||||
|
||||
internal IDictionary<Selector, Style> Styles { get; set; } = new Dictionary<Selector, Style>();
|
||||
|
||||
public static StyleSheet FromAssemblyResource(Assembly assembly, string resourceId, IXmlLineInfo lineInfo = null)
|
||||
{
|
||||
using (var stream = assembly.GetManifestResourceStream(resourceId)) {
|
||||
if (stream == null)
|
||||
throw new XamlParseException($"No resource found for '{resourceId}'.", lineInfo);
|
||||
using (var reader = new StreamReader(stream)) {
|
||||
return FromReader(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//used by code generated by XamlC. Has to stay public
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static StyleSheet FromString(string stylesheet)
|
||||
{
|
||||
if (stylesheet == null)
|
||||
throw new ArgumentNullException(nameof(stylesheet));
|
||||
using (var reader = new StringReader(stylesheet))
|
||||
return FromReader(reader);
|
||||
}
|
||||
|
||||
public static StyleSheet FromReader(TextReader reader)
|
||||
{
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException(nameof(reader));
|
||||
|
||||
return Parse(new CssReader(reader));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static StyleSheet Parse(CssReader reader)
|
||||
{
|
||||
var sheet = new StyleSheet();
|
||||
|
||||
Style style = null;
|
||||
var selector = Selector.All;
|
||||
|
||||
int p;
|
||||
bool inStyle = false;
|
||||
reader.SkipWhiteSpaces();
|
||||
while ((p = reader.Peek()) > 0) {
|
||||
switch ((char)p) {
|
||||
case '@':
|
||||
throw new NotSupportedException("AT-rules not supported");
|
||||
case '{':
|
||||
reader.Read();
|
||||
style = Style.Parse(reader, '}');
|
||||
inStyle = true;
|
||||
break;
|
||||
case '}':
|
||||
reader.Read();
|
||||
if (!inStyle)
|
||||
throw new Exception();
|
||||
inStyle = false;
|
||||
sheet.Styles.Add(selector, style);
|
||||
style = null;
|
||||
selector = Selector.All;
|
||||
break;
|
||||
default:
|
||||
selector = Selector.Parse(reader, '{');
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sheet;
|
||||
}
|
||||
|
||||
Type IStyle.TargetType
|
||||
=> typeof(VisualElement);
|
||||
|
||||
void IStyle.Apply(BindableObject bindable)
|
||||
{
|
||||
var styleable = bindable as VisualElement;
|
||||
if (styleable == null)
|
||||
return;
|
||||
Apply(styleable);
|
||||
}
|
||||
|
||||
void Apply(VisualElement styleable)
|
||||
{
|
||||
ApplyCore(styleable);
|
||||
foreach (var child in styleable.LogicalChildrenInternal)
|
||||
((IStyle)this).Apply(child);
|
||||
}
|
||||
|
||||
void ApplyCore(VisualElement styleable)
|
||||
{
|
||||
foreach (var kvp in Styles) {
|
||||
var selector = kvp.Key;
|
||||
var style = kvp.Value;
|
||||
if (!selector.Matches(styleable))
|
||||
continue;
|
||||
style.Apply(styleable);
|
||||
}
|
||||
}
|
||||
|
||||
void IStyle.UnApply(BindableObject bindable)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets
|
||||
{
|
||||
static class StyleSheetExtensions
|
||||
{
|
||||
public static IEnumerable<StyleSheet> GetStyleSheets(this IResourcesProvider resourcesProvider)
|
||||
{
|
||||
if (!resourcesProvider.IsResourcesCreated)
|
||||
yield break;
|
||||
if (resourcesProvider.Resources.StyleSheets == null)
|
||||
yield break;
|
||||
foreach (var styleSheet in resourcesProvider.Resources.StyleSheets)
|
||||
yield return styleSheet;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets
|
||||
{
|
||||
class StyleSheetServiceProvider : IServiceProvider
|
||||
{
|
||||
IProvideValueTarget vtProvider;
|
||||
IConverterOptions convOptions => new ConverterOptions();
|
||||
|
||||
public StyleSheetServiceProvider(object targetObject, object targetProperty)
|
||||
{
|
||||
vtProvider = new ValueTargetProvider {
|
||||
TargetObject = targetObject,
|
||||
TargetProperty = targetProperty
|
||||
};
|
||||
}
|
||||
|
||||
public object GetService(Type serviceType)
|
||||
{
|
||||
if (serviceType == typeof(IProvideValueTarget))
|
||||
return vtProvider;
|
||||
if (serviceType == typeof(IConverterOptions))
|
||||
return convOptions;
|
||||
return null;
|
||||
}
|
||||
|
||||
class ValueTargetProvider : IProvideValueTarget
|
||||
{
|
||||
public object TargetObject { get; set; }
|
||||
public object TargetProperty { get; set; }
|
||||
}
|
||||
|
||||
class ConverterOptions : IConverterOptions
|
||||
{
|
||||
public bool IgnoreCase => true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Xamarin.Forms.StyleSheets
|
||||
{
|
||||
static class TextReaderExtensions
|
||||
{
|
||||
//ident [-]?{nmstart}{nmchar}*
|
||||
public static string ReadIdent(this TextReader reader)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
bool first = true;
|
||||
bool hasLeadingDash = false;
|
||||
int p;
|
||||
while ((p = reader.Peek()) > 0) {
|
||||
var c = unchecked((char)p);
|
||||
if (first && !hasLeadingDash && c == '-') {
|
||||
sb.Append((char)reader.Read());
|
||||
hasLeadingDash = true;
|
||||
} else if (first && c.IsNmStart()) {
|
||||
sb.Append((char)reader.Read());
|
||||
first = false;
|
||||
} else if (first) { //a nmstart is expected
|
||||
throw new Exception();
|
||||
} else if (c.IsNmChar())
|
||||
sb.Append((char)reader.Read());
|
||||
else
|
||||
break;
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
//name {nmchar}+
|
||||
public static string ReadName(this TextReader reader)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
int p;
|
||||
while ((p = reader.Peek()) > 0) {
|
||||
var c = unchecked((char)p);
|
||||
if (c.IsNmChar())
|
||||
sb.Append((char)reader.Read());
|
||||
else
|
||||
break;
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string ReadUntil(this TextReader reader, params char[] limit)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
int p;
|
||||
while ((p = reader.Peek()) > 0) {
|
||||
var c = unchecked((char)p);
|
||||
if (limit != null && limit.Contains(c))
|
||||
break;
|
||||
reader.Read();
|
||||
sb.Append(c);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
//w [ \t\r\n\f]*
|
||||
public static void SkipWhiteSpaces(this TextReader reader)
|
||||
{
|
||||
int p;
|
||||
while ((p = reader.Peek()) > 0) {
|
||||
var c = unchecked((char)p);
|
||||
if (!c.IsW())
|
||||
break;
|
||||
reader.Read();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,32 @@
|
|||
using System;
|
||||
|
||||
namespace Xamarin.Forms
|
||||
{
|
||||
[TypeConverter(typeof(TextAlignmentConverter))]
|
||||
public enum TextAlignment
|
||||
{
|
||||
Start,
|
||||
Center,
|
||||
End
|
||||
}
|
||||
|
||||
[Xaml.TypeConversion(typeof(TextAlignment))]
|
||||
public class TextAlignmentConverter : TypeConverter
|
||||
{
|
||||
public override object ConvertFromInvariantString(string value)
|
||||
{
|
||||
if (value != null) {
|
||||
if (Enum.TryParse(value, out TextAlignment direction))
|
||||
return direction;
|
||||
|
||||
if (value.Equals("left", StringComparison.OrdinalIgnoreCase))
|
||||
return TextAlignment.Start;
|
||||
if (value.Equals("right", StringComparison.OrdinalIgnoreCase))
|
||||
return TextAlignment.End;
|
||||
if (value.Equals("center", StringComparison.OrdinalIgnoreCase))
|
||||
return TextAlignment.Center;
|
||||
}
|
||||
throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(TextAlignment)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ namespace Xamarin.Forms
|
|||
static class TextElement
|
||||
{
|
||||
public static readonly BindableProperty TextColorProperty =
|
||||
BindableProperty.Create("TextColor", typeof(Color), typeof(ITextElement), Color.Default,
|
||||
BindableProperty.Create(nameof(ITextElement.TextColor), typeof(Color), typeof(ITextElement), Color.Default,
|
||||
propertyChanged: OnTextColorPropertyChanged);
|
||||
|
||||
static void OnTextColorPropertyChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
|
|
|
@ -9,25 +9,51 @@ namespace Xamarin.Forms
|
|||
{
|
||||
public override object ConvertFromInvariantString(string value)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
double l, t, r, b;
|
||||
string[] thickness = value.Split(',');
|
||||
switch (thickness.Length)
|
||||
{
|
||||
case 1:
|
||||
if (double.TryParse(thickness[0], NumberStyles.Number, CultureInfo.InvariantCulture, out l))
|
||||
return new Thickness(l);
|
||||
break;
|
||||
if (value != null) {
|
||||
value = value.Trim();
|
||||
if (value.Contains(",")) { //Xaml
|
||||
var thickness = value.Split(',');
|
||||
switch (thickness.Length) {
|
||||
case 2:
|
||||
if (double.TryParse(thickness[0], NumberStyles.Number, CultureInfo.InvariantCulture, out l) && double.TryParse(thickness[1], NumberStyles.Number, CultureInfo.InvariantCulture, out t))
|
||||
return new Thickness(l, t);
|
||||
if ( double.TryParse(thickness[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double h)
|
||||
&& double.TryParse(thickness[1], NumberStyles.Number, CultureInfo.InvariantCulture, out double v))
|
||||
return new Thickness(h, v);
|
||||
break;
|
||||
case 4:
|
||||
if (double.TryParse(thickness[0], NumberStyles.Number, CultureInfo.InvariantCulture, out l) && double.TryParse(thickness[1], NumberStyles.Number, CultureInfo.InvariantCulture, out t) &&
|
||||
double.TryParse(thickness[2], NumberStyles.Number, CultureInfo.InvariantCulture, out r) && double.TryParse(thickness[3], NumberStyles.Number, CultureInfo.InvariantCulture, out b))
|
||||
if ( double.TryParse(thickness[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double l)
|
||||
&& double.TryParse(thickness[1], NumberStyles.Number, CultureInfo.InvariantCulture, out double t)
|
||||
&& double.TryParse(thickness[2], NumberStyles.Number, CultureInfo.InvariantCulture, out double r)
|
||||
&& double.TryParse(thickness[3], NumberStyles.Number, CultureInfo.InvariantCulture, out double b))
|
||||
return new Thickness(l, t, r, b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (value.Contains(" ")) { //CSS
|
||||
var thickness = value.Split(' ');
|
||||
switch (thickness.Length) {
|
||||
case 2:
|
||||
if ( double.TryParse(thickness[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double v)
|
||||
&& double.TryParse(thickness[1], NumberStyles.Number, CultureInfo.InvariantCulture, out double h))
|
||||
return new Thickness(h, v);
|
||||
break;
|
||||
case 3:
|
||||
if ( double.TryParse(thickness[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double t)
|
||||
&& double.TryParse(thickness[1], NumberStyles.Number, CultureInfo.InvariantCulture, out h)
|
||||
&& double.TryParse(thickness[2], NumberStyles.Number, CultureInfo.InvariantCulture, out double b))
|
||||
return new Thickness(h, t, h, b);
|
||||
break;
|
||||
case 4:
|
||||
if ( double.TryParse(thickness[0], NumberStyles.Number, CultureInfo.InvariantCulture, out t)
|
||||
&& double.TryParse(thickness[1], NumberStyles.Number, CultureInfo.InvariantCulture, out double r)
|
||||
&& double.TryParse(thickness[2], NumberStyles.Number, CultureInfo.InvariantCulture, out b)
|
||||
&& double.TryParse(thickness[3], NumberStyles.Number, CultureInfo.InvariantCulture, out double l))
|
||||
return new Thickness(l, t, r, b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else { //single uniform thickness
|
||||
if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out double l))
|
||||
return new Thickness(l);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -216,6 +216,7 @@ namespace Xamarin.Forms
|
|||
get { return (bool)GetValue(IsFocusedProperty); }
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(VisibilityConverter))]
|
||||
public bool IsVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsVisibleProperty); }
|
||||
|
@ -650,6 +651,7 @@ namespace Xamarin.Forms
|
|||
#pragma warning restore 0618
|
||||
|
||||
FlowController.NotifyFlowDirectionChanged();
|
||||
ApplyStyleSheetsOnParentSet();
|
||||
}
|
||||
|
||||
protected virtual void OnSizeAllocated(double width, double height)
|
||||
|
@ -727,6 +729,22 @@ namespace Xamarin.Forms
|
|||
InvalidateMeasureInternal(InvalidationTrigger.Undefined);
|
||||
}
|
||||
|
||||
internal override void OnParentResourcesChanged(object sender, ResourcesChangedEventArgs e)
|
||||
{
|
||||
if (e == ResourcesChangedEventArgs.StyleSheets)
|
||||
ApplyStyleSheetsOnParentSet();
|
||||
else
|
||||
base.OnParentResourcesChanged(sender, e);
|
||||
}
|
||||
|
||||
internal override void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
|
||||
{
|
||||
if (e == ResourcesChangedEventArgs.StyleSheets)
|
||||
ApplyStyleSheets();
|
||||
else
|
||||
base.OnResourcesChanged(sender, e);
|
||||
}
|
||||
|
||||
internal override void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
|
||||
{
|
||||
if (values == null)
|
||||
|
@ -854,5 +872,26 @@ namespace Xamarin.Forms
|
|||
|
||||
public bool Result { get; set; }
|
||||
}
|
||||
|
||||
public class VisibilityConverter : TypeConverter
|
||||
{
|
||||
public override object ConvertFromInvariantString(string value)
|
||||
{
|
||||
if (value != null) {
|
||||
if (value.Equals("true", StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
if (value.Equals("visible", StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
if (value.Equals("false", StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
if (value.Equals("hidden", StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
if (value.Equals("collapse", StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
}
|
||||
throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(bool)));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
using Xamarin.Forms.Internals;
|
||||
using Xamarin.Forms.StyleSheets;
|
||||
|
||||
namespace Xamarin.Forms
|
||||
{
|
||||
public partial class VisualElement : IStyleSelectable, IStylable
|
||||
{
|
||||
IList<string> IStyleSelectable.Classes
|
||||
=> StyleClass;
|
||||
|
||||
BindableProperty IStylable.GetProperty(string key)
|
||||
{
|
||||
StylePropertyAttribute styleAttribute;
|
||||
if (!Internals.Registrar.StyleProperties.TryGetValue(key, out styleAttribute))
|
||||
return null;
|
||||
|
||||
if (!styleAttribute.TargetType.GetTypeInfo().IsAssignableFrom(GetType().GetTypeInfo()))
|
||||
return null;
|
||||
|
||||
if (styleAttribute.BindableProperty != null)
|
||||
return styleAttribute.BindableProperty;
|
||||
|
||||
var propertyOwnerType = styleAttribute.PropertyOwnerType ?? GetType();
|
||||
var bpField = propertyOwnerType.GetField(styleAttribute.BindablePropertyName,
|
||||
BindingFlags.Public
|
||||
| BindingFlags.NonPublic
|
||||
| BindingFlags.Static
|
||||
| BindingFlags.FlattenHierarchy);
|
||||
if (bpField == null)
|
||||
return null;
|
||||
|
||||
return (styleAttribute.BindableProperty = bpField.GetValue(null) as BindableProperty);
|
||||
}
|
||||
|
||||
void ApplyStyleSheets()
|
||||
{
|
||||
foreach (var styleSheet in this.GetStyleSheets())
|
||||
((IStyle)styleSheet).Apply(this);
|
||||
}
|
||||
|
||||
//on parent set, or on parent stylesheet changed, reapply all
|
||||
void ApplyStyleSheetsOnParentSet()
|
||||
{
|
||||
var parent = Parent;
|
||||
if (parent == null)
|
||||
return;
|
||||
var sheets = new List<StyleSheet>();
|
||||
while (parent != null) {
|
||||
var visualParent = parent as VisualElement;
|
||||
var vpSheets = visualParent?.GetStyleSheets();
|
||||
if (vpSheets != null)
|
||||
sheets.AddRange(vpSheets);
|
||||
parent = parent.Parent;
|
||||
}
|
||||
for (var i = sheets.Count - 1; i >= 0; i--)
|
||||
((IStyle)sheets[i]).Apply(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using System.IO;
|
||||
|
||||
namespace Xamarin.Forms
|
||||
{
|
||||
interface IResourcesLoader
|
||||
{
|
||||
ResourceDictionary CreateResourceDictionary(string resourcePath, Assembly assembly, IXmlLineInfo lineInfo);
|
||||
T CreateFromResource<T>(string resourcePath, Assembly assembly, IXmlLineInfo lineInfo) where T : new();
|
||||
string GetResource(string resourcePath, Assembly assembly, IXmlLineInfo lineInfo);
|
||||
}
|
||||
}
|
|
@ -124,6 +124,7 @@ namespace Xamarin.Forms.Xaml
|
|||
if (convertFromStringInvariant != null)
|
||||
return value = convertFromStringInvariant.Invoke(converter, new object[] { str });
|
||||
}
|
||||
var ignoreCase = (serviceProvider?.GetService(typeof(IConverterOptions)) as IConverterOptions)?.IgnoreCase ?? false;
|
||||
|
||||
//If the type is nullable, as the value is not null, it's safe to assume we want the built-in conversion
|
||||
if (toType.GetTypeInfo().IsGenericType && toType.GetGenericTypeDefinition() == typeof (Nullable<>))
|
||||
|
@ -131,7 +132,7 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
//Obvious Built-in conversions
|
||||
if (toType.GetTypeInfo().IsEnum)
|
||||
return Enum.Parse(toType, str);
|
||||
return Enum.Parse(toType, str, ignoreCase);
|
||||
if (toType == typeof(SByte))
|
||||
return SByte.Parse(str, CultureInfo.InvariantCulture);
|
||||
if (toType == typeof(Int16))
|
||||
|
|
|
@ -201,7 +201,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
paint.StrokeWidth = 1;
|
||||
paint.SetStyle(style);
|
||||
paint.Color = _frame.OutlineColor.ToAndroid();
|
||||
paint.Color = _frame.BorderColor.ToAndroid();
|
||||
|
||||
canvas.DrawPath(path, paint);
|
||||
}
|
||||
|
@ -209,9 +209,9 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
void FrameOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName ||
|
||||
e.PropertyName == Frame.OutlineColorProperty.PropertyName ||
|
||||
e.PropertyName == Frame.CornerRadiusProperty.PropertyName)
|
||||
if ( e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName
|
||||
|| e.PropertyName == Frame.BorderColorProperty.PropertyName
|
||||
|| e.PropertyName == Frame.CornerRadiusProperty.PropertyName)
|
||||
{
|
||||
if(_normalBitmap == null)
|
||||
return;
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Xamarin.Forms.Platform.MacOS
|
|||
base.OnElementPropertyChanged(sender, e);
|
||||
|
||||
if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName ||
|
||||
e.PropertyName == Xamarin.Forms.Frame.OutlineColorProperty.PropertyName ||
|
||||
e.PropertyName == Xamarin.Forms.Frame.BorderColorProperty.PropertyName ||
|
||||
e.PropertyName == Xamarin.Forms.Frame.HasShadowProperty.PropertyName)
|
||||
SetupLayer();
|
||||
}
|
||||
|
@ -42,11 +42,11 @@ namespace Xamarin.Forms.Platform.MacOS
|
|||
else
|
||||
Layer.ShadowOpacity = 0;
|
||||
|
||||
if (Element.OutlineColor == Color.Default)
|
||||
if (Element.BorderColor == Color.Default)
|
||||
Layer.BorderColor = NSColor.Clear.CGColor;
|
||||
else
|
||||
{
|
||||
Layer.BorderColor = Element.OutlineColor.ToCGColor();
|
||||
Layer.BorderColor = Element.BorderColor.ToCGColor();
|
||||
Layer.BorderWidth = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace Xamarin.Forms.Platform.UWP
|
|||
{
|
||||
PackChild();
|
||||
}
|
||||
else if (e.PropertyName == Frame.OutlineColorProperty.PropertyName || e.PropertyName == Frame.HasShadowProperty.PropertyName)
|
||||
else if (e.PropertyName == Frame.BorderColorProperty.PropertyName || e.PropertyName == Frame.HasShadowProperty.PropertyName)
|
||||
{
|
||||
UpdateBorder();
|
||||
}
|
||||
|
@ -67,9 +67,9 @@ namespace Xamarin.Forms.Platform.UWP
|
|||
|
||||
void UpdateBorder()
|
||||
{
|
||||
if (Element.OutlineColor != Color.Default)
|
||||
if (Element.BorderColor != Color.Default)
|
||||
{
|
||||
Control.BorderBrush = Element.OutlineColor.ToBrush();
|
||||
Control.BorderBrush = Element.BorderColor.ToBrush();
|
||||
Control.BorderThickness = new Windows.UI.Xaml.Thickness(1);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
base.OnElementPropertyChanged(sender, e);
|
||||
|
||||
if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName ||
|
||||
e.PropertyName == Xamarin.Forms.Frame.OutlineColorProperty.PropertyName ||
|
||||
e.PropertyName == Xamarin.Forms.Frame.BorderColorProperty.PropertyName ||
|
||||
e.PropertyName == Xamarin.Forms.Frame.HasShadowProperty.PropertyName ||
|
||||
e.PropertyName == Xamarin.Forms.Frame.CornerRadiusProperty.PropertyName)
|
||||
SetupLayer();
|
||||
|
@ -49,11 +49,11 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
else
|
||||
Layer.ShadowOpacity = 0;
|
||||
|
||||
if (Element.OutlineColor == Color.Default)
|
||||
if (Element.BorderColor == Color.Default)
|
||||
Layer.BorderColor = UIColor.Clear.CGColor;
|
||||
else
|
||||
{
|
||||
Layer.BorderColor = Element.OutlineColor.ToCGColor();
|
||||
Layer.BorderColor = Element.BorderColor.ToCGColor();
|
||||
Layer.BorderWidth = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<ContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Xamarin.Forms.Xaml.UnitTests.InlineCSS">
|
||||
<ContentPage.Resources>
|
||||
<StyleSheet>
|
||||
<![CDATA[
|
||||
* {
|
||||
background-color: green;
|
||||
}
|
||||
label {
|
||||
color: pink;
|
||||
background-color: initial;
|
||||
}
|
||||
]]>
|
||||
</StyleSheet>
|
||||
</ContentPage.Resources>
|
||||
<StackLayout x:Name="stack">
|
||||
<Label x:Name="label"/>
|
||||
<Button x:Name="button"/>
|
||||
</StackLayout>
|
||||
</ContentPage>
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms.Core.UnitTests;
|
||||
|
||||
namespace Xamarin.Forms.Xaml.UnitTests
|
||||
{
|
||||
public partial class InlineCSS : ContentPage
|
||||
{
|
||||
public InlineCSS()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public InlineCSS(bool useCompiledXaml)
|
||||
{
|
||||
//this stub will be replaced at compile time
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
public class Tests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Device.PlatformServices = new MockPlatformServices();
|
||||
Xamarin.Forms.Internals.Registrar.RegisterAll(new Type[0]);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Device.PlatformServices = null;
|
||||
}
|
||||
|
||||
[TestCase(false), TestCase(true)]
|
||||
public void InlineCSSParsed(bool useCompiledXaml)
|
||||
{
|
||||
var layout = new InlineCSS(useCompiledXaml);
|
||||
Assert.That(layout.label.TextColor, Is.EqualTo(Color.Pink));
|
||||
}
|
||||
|
||||
[TestCase(false), TestCase(true)]
|
||||
public void InitialValue(bool useCompiledXaml)
|
||||
{
|
||||
var layout = new InlineCSS(useCompiledXaml);
|
||||
Assert.That(layout.BackgroundColor, Is.EqualTo(Color.Green));
|
||||
Assert.That(layout.stack.BackgroundColor, Is.EqualTo(Color.Green));
|
||||
Assert.That(layout.button.BackgroundColor, Is.EqualTo(Color.Green));
|
||||
Assert.That(layout.label.BackgroundColor, Is.EqualTo(VisualElement.BackgroundColorProperty.DefaultValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<ContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Xamarin.Forms.Xaml.UnitTests.SetStyleIdFromXName">
|
||||
<StackLayout>
|
||||
<Label x:Name="label0" />
|
||||
<Label x:Name="label1" StyleId="foo" />
|
||||
<Label StyleId="bar" x:Name="label2" />
|
||||
</StackLayout>
|
||||
</ContentPage>
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms.Core.UnitTests;
|
||||
|
||||
namespace Xamarin.Forms.Xaml.UnitTests
|
||||
{
|
||||
public partial class SetStyleIdFromXName : ContentPage
|
||||
{
|
||||
public SetStyleIdFromXName()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public SetStyleIdFromXName(bool useCompiledXaml)
|
||||
{
|
||||
//this stub will be replaced at compile time
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
public class Tests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Device.PlatformServices = new MockPlatformServices();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Device.PlatformServices = null;
|
||||
}
|
||||
|
||||
[TestCase(false), TestCase(true)]
|
||||
public void SetStyleId(bool useCompiledXaml)
|
||||
{
|
||||
var layout = new SetStyleIdFromXName(useCompiledXaml);
|
||||
Assert.That(layout.label0.StyleId, Is.EqualTo("label0"));
|
||||
Assert.That(layout.label1.StyleId, Is.EqualTo("foo"));
|
||||
Assert.That(layout.label2.StyleId, Is.EqualTo("bar"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<ContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Xamarin.Forms.Xaml.UnitTests.StyleSheet">
|
||||
<ContentPage.Resources>
|
||||
<StyleSheet Source="css/foo.css" />
|
||||
</ContentPage.Resources>
|
||||
<StackLayout>
|
||||
<Label x:Name="label0" />
|
||||
</StackLayout>
|
||||
</ContentPage>
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
using Xamarin.Forms.Core.UnitTests;
|
||||
using NUnit.Framework.Constraints;
|
||||
|
||||
namespace Xamarin.Forms.Xaml.UnitTests
|
||||
{
|
||||
public partial class StyleSheet : ContentPage
|
||||
{
|
||||
public StyleSheet()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public StyleSheet(bool useCompiledXaml)
|
||||
{
|
||||
//this stub will be replaced at compile time
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
public class Tests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Device.PlatformServices = new MockPlatformServices();
|
||||
Xamarin.Forms.Internals.Registrar.RegisterAll(new Type[0]);
|
||||
}
|
||||
|
||||
[TestCase(false), TestCase(true)]
|
||||
public void EmbeddedStyleSheetsAreLoaded(bool useCompiledXaml)
|
||||
{
|
||||
var layout = new StyleSheet(useCompiledXaml);
|
||||
Assert.That(layout.Resources.StyleSheets[0].Styles.Count, Is.GreaterThanOrEqualTo(1));
|
||||
}
|
||||
|
||||
[TestCase(false), TestCase(true)]
|
||||
public void StyleSheetsAreApplied(bool useCompiledXaml)
|
||||
{
|
||||
var layout = new StyleSheet(useCompiledXaml);
|
||||
Assert.That(layout.label0.TextColor, Is.EqualTo(Color.Azure));
|
||||
Assert.That(layout.label0.BackgroundColor, Is.EqualTo(Color.AliceBlue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -463,7 +463,6 @@
|
|||
</Compile>
|
||||
<Compile Include="Issues\Bz44216.xaml.cs">
|
||||
<DependentUpon>Bz44216.xaml</DependentUpon>
|
||||
<DependentUpon>AcceptEmptyServiceProvider.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="AcceptEmptyServiceProvider.xaml.cs">
|
||||
<DependentUpon>AcceptEmptyServiceProvider.xaml</DependentUpon>
|
||||
|
@ -553,6 +552,15 @@
|
|||
<Compile Include="Issues\Gh1346.xaml.cs">
|
||||
<DependentUpon>Gh1346.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="StyleSheet.xaml.cs">
|
||||
<DependentUpon>StyleSheet.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SetStyleIdFromXName.xaml.cs">
|
||||
<DependentUpon>SetStyleIdFromXName.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="InlineCSS.xaml.cs">
|
||||
<DependentUpon>InlineCSS.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\.nuspec\Xamarin.Forms.Debug.targets" />
|
||||
|
@ -582,15 +590,6 @@
|
|||
<Name>Xamarin.Forms.Maps</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ProjectExtensions>
|
||||
<MonoDevelop>
|
||||
<Properties>
|
||||
<Policies>
|
||||
<StandardHeader Text="" IncludeInNewFiles="True" />
|
||||
</Policies>
|
||||
</Properties>
|
||||
</MonoDevelop>
|
||||
</ProjectExtensions>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="CustomXamlView.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
|
@ -1011,24 +1010,39 @@
|
|||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="css\foo.css" />
|
||||
<EmbeddedResource Include="css\bar.css" />
|
||||
<EmbeddedResource Include="StyleSheet.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Issues\Bz41296.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="PlatformSpecifics.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Issues\Bz43450.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="AutomationProperties.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="SetStyleIdFromXName.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="InlineCSS.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="css\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
label {
|
||||
color: azure;
|
||||
background-color: aliceblue;
|
||||
}
|
|
@ -602,6 +602,8 @@ namespace Xamarin.Forms.Xaml
|
|||
resourceDictionary.Add((Style)value);
|
||||
else if (value is ResourceDictionary)
|
||||
resourceDictionary.Add((ResourceDictionary)value);
|
||||
else if (value is StyleSheets.StyleSheet)
|
||||
resourceDictionary.Add((StyleSheets.StyleSheet)value);
|
||||
else {
|
||||
exception = new XamlParseException("resources in ResourceDictionary require a x:Key attribute", lineInfo);
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using Xamarin.Forms.StyleSheets;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
|
||||
namespace Xamarin.Forms.Xaml
|
||||
{
|
||||
[ContentProperty(nameof(Style))]
|
||||
[ProvideCompiled("Xamarin.Forms.Core.XamlC.StyleSheetProvider")]
|
||||
public sealed class StyleSheetExtension : IValueProvider
|
||||
{
|
||||
public string Style { get; set; }
|
||||
public Uri Source { get; set; }
|
||||
|
||||
object IValueProvider.ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
IXmlLineInfo lineInfo;
|
||||
|
||||
if (!string.IsNullOrEmpty(Style) && Source != null) {
|
||||
lineInfo = (serviceProvider.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider)?.XmlLineInfo;
|
||||
throw new XamlParseException($"StyleSheet can not have both a Source and a content", lineInfo);
|
||||
}
|
||||
|
||||
if (Source != null) {
|
||||
lineInfo = (serviceProvider.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider)?.XmlLineInfo;
|
||||
if (Source.IsAbsoluteUri)
|
||||
throw new XamlParseException($"Source only accepts Relative URIs", lineInfo);
|
||||
|
||||
var rootObjectType = (serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider)?.RootObject.GetType();
|
||||
if (rootObjectType == null)
|
||||
return null;
|
||||
var rootTargetPath = XamlResourceIdAttribute.GetPathForType(rootObjectType);
|
||||
var resourcePath = ResourceDictionary.RDSourceTypeConverter.GetResourcePath(Source, rootTargetPath);
|
||||
var resString = DependencyService.Get<IResourcesLoader>().GetResource(resourcePath, rootObjectType.GetTypeInfo().Assembly, lineInfo);
|
||||
return StyleSheet.FromString(resString);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Style)) {
|
||||
using (var reader = new StringReader(Style))
|
||||
return StyleSheet.FromReader(reader);
|
||||
}
|
||||
|
||||
lineInfo = (serviceProvider.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider)?.XmlLineInfo;
|
||||
throw new XamlParseException($"StyleSheet require either a Source or a content", lineInfo);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,6 +31,9 @@ namespace Xamarin.Forms.Xaml
|
|||
throw ae;
|
||||
throw new XamlParseException($"An element with the name \"{(string)node.Value}\" already exists in this NameScope", node);
|
||||
}
|
||||
var element = Values[parentNode] as Element;
|
||||
if (element != null)
|
||||
element.StyleId = element.StyleId ?? (string)node.Value;
|
||||
}
|
||||
|
||||
public void Visit(MarkupNode node, INode parentNode)
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace Xamarin.Forms.Xaml
|
|||
{
|
||||
class ResourcesLoader : IResourcesLoader
|
||||
{
|
||||
public ResourceDictionary CreateResourceDictionary(string resourcePath, Assembly assembly, IXmlLineInfo lineInfo)
|
||||
public T CreateFromResource<T>(string resourcePath, Assembly assembly, IXmlLineInfo lineInfo) where T: new()
|
||||
{
|
||||
var resourceId = XamlResourceIdAttribute.GetResourceIdForPath(assembly, resourcePath);
|
||||
if (resourceId == null)
|
||||
|
@ -17,7 +17,7 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
var alternateResource = Xamarin.Forms.Internals.ResourceLoader.ResourceProvider?.Invoke(resourcePath);
|
||||
if (alternateResource != null) {
|
||||
var rd = new ResourceDictionary();
|
||||
var rd = new T();
|
||||
rd.LoadFromXaml(alternateResource);
|
||||
return rd;
|
||||
}
|
||||
|
@ -26,11 +26,29 @@ namespace Xamarin.Forms.Xaml
|
|||
if (stream == null)
|
||||
throw new XamlParseException($"No resource found for '{resourceId}'.", lineInfo);
|
||||
using (var reader = new StreamReader(stream)) {
|
||||
var rd = new ResourceDictionary();
|
||||
var rd = new T();
|
||||
rd.LoadFromXaml(reader.ReadToEnd());
|
||||
return rd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GetResource(string resourcePath, Assembly assembly, IXmlLineInfo lineInfo)
|
||||
{
|
||||
var resourceId = XamlResourceIdAttribute.GetResourceIdForPath(assembly, resourcePath);
|
||||
if (resourceId == null)
|
||||
throw new XamlParseException($"Resource '{resourcePath}' not found.", lineInfo);
|
||||
|
||||
var alternateResource = Xamarin.Forms.Internals.ResourceLoader.ResourceProvider?.Invoke(resourcePath);
|
||||
if (alternateResource != null)
|
||||
return alternateResource;
|
||||
|
||||
using (var stream = assembly.GetManifestResourceStream(resourceId)) {
|
||||
if (stream == null)
|
||||
throw new XamlParseException($"No resource found for '{resourceId}'.", lineInfo);
|
||||
using (var reader = new StreamReader(stream))
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,6 +35,21 @@
|
|||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="StyleSheets">
|
||||
<MemberSignature Language="C#" Value="public static readonly Xamarin.Forms.Internals.ResourcesChangedEventArgs StyleSheets;" />
|
||||
<MemberSignature Language="ILAsm" Value=".field public static initonly class Xamarin.Forms.Internals.ResourcesChangedEventArgs StyleSheets" />
|
||||
<MemberType>Field</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>Xamarin.Forms.Internals.ResourcesChangedEventArgs</ReturnType>
|
||||
</ReturnValue>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="Values">
|
||||
<MemberSignature Language="C#" Value="public System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string,object>> Values { get; }" />
|
||||
<MemberSignature Language="ILAsm" Value=".property instance class System.Collections.Generic.IEnumerable`1<valuetype System.Collections.Generic.KeyValuePair`2<string, object>> Values" />
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
<Type Name="StyleSheet" FullName="Xamarin.Forms.StyleSheets.StyleSheet">
|
||||
<TypeSignature Language="C#" Value="public sealed class StyleSheet" />
|
||||
<TypeSignature Language="ILAsm" Value=".class public auto ansi sealed beforefieldinit StyleSheet extends System.Object" />
|
||||
<AssemblyInfo>
|
||||
<AssemblyName>Xamarin.Forms.Core</AssemblyName>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Base>
|
||||
<BaseTypeName>System.Object</BaseTypeName>
|
||||
</Base>
|
||||
<Interfaces />
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
<Members>
|
||||
<Member MemberName="FromAssemblyResource">
|
||||
<MemberSignature Language="C#" Value="public static Xamarin.Forms.StyleSheets.StyleSheet FromAssemblyResource (System.Reflection.Assembly assembly, string resourceId, System.Xml.IXmlLineInfo lineInfo = null);" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public static hidebysig class Xamarin.Forms.StyleSheets.StyleSheet FromAssemblyResource(class System.Reflection.Assembly assembly, string resourceId, class System.Xml.IXmlLineInfo lineInfo) cil managed" />
|
||||
<MemberType>Method</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>Xamarin.Forms.StyleSheets.StyleSheet</ReturnType>
|
||||
</ReturnValue>
|
||||
<Parameters>
|
||||
<Parameter Name="assembly" Type="System.Reflection.Assembly" />
|
||||
<Parameter Name="resourceId" Type="System.String" />
|
||||
<Parameter Name="lineInfo" Type="System.Xml.IXmlLineInfo" />
|
||||
</Parameters>
|
||||
<Docs>
|
||||
<param name="assembly">To be added.</param>
|
||||
<param name="resourceId">To be added.</param>
|
||||
<param name="lineInfo">To be added.</param>
|
||||
<summary>To be added.</summary>
|
||||
<returns>To be added.</returns>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="FromReader">
|
||||
<MemberSignature Language="C#" Value="public static Xamarin.Forms.StyleSheets.StyleSheet FromReader (System.IO.TextReader reader);" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public static hidebysig class Xamarin.Forms.StyleSheets.StyleSheet FromReader(class System.IO.TextReader reader) cil managed" />
|
||||
<MemberType>Method</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>Xamarin.Forms.StyleSheets.StyleSheet</ReturnType>
|
||||
</ReturnValue>
|
||||
<Parameters>
|
||||
<Parameter Name="reader" Type="System.IO.TextReader" />
|
||||
</Parameters>
|
||||
<Docs>
|
||||
<param name="reader">To be added.</param>
|
||||
<summary>To be added.</summary>
|
||||
<returns>To be added.</returns>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="FromString">
|
||||
<MemberSignature Language="C#" Value="public static Xamarin.Forms.StyleSheets.StyleSheet FromString (string stylesheet);" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public static hidebysig class Xamarin.Forms.StyleSheets.StyleSheet FromString(string stylesheet) cil managed" />
|
||||
<MemberType>Method</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<AttributeName>System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)</AttributeName>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
<ReturnValue>
|
||||
<ReturnType>Xamarin.Forms.StyleSheets.StyleSheet</ReturnType>
|
||||
</ReturnValue>
|
||||
<Parameters>
|
||||
<Parameter Name="stylesheet" Type="System.String" />
|
||||
</Parameters>
|
||||
<Docs>
|
||||
<param name="stylesheet">To be added.</param>
|
||||
<summary>To be added.</summary>
|
||||
<returns>To be added.</returns>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
</Members>
|
||||
</Type>
|
|
@ -8,6 +8,11 @@
|
|||
<Base>
|
||||
<BaseTypeName>System.Enum</BaseTypeName>
|
||||
</Base>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<AttributeName>Xamarin.Forms.TypeConverter(typeof(Xamarin.Forms.FlowDirectionConverter))</AttributeName>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<Type Name="FlowDirectionConverter" FullName="Xamarin.Forms.FlowDirectionConverter">
|
||||
<TypeSignature Language="C#" Value="public class FlowDirectionConverter : Xamarin.Forms.TypeConverter" />
|
||||
<TypeSignature Language="ILAsm" Value=".class public auto ansi beforefieldinit FlowDirectionConverter extends Xamarin.Forms.TypeConverter" />
|
||||
<AssemblyInfo>
|
||||
<AssemblyName>Xamarin.Forms.Core</AssemblyName>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Base>
|
||||
<BaseTypeName>Xamarin.Forms.TypeConverter</BaseTypeName>
|
||||
</Base>
|
||||
<Interfaces />
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<AttributeName>Xamarin.Forms.Xaml.TypeConversion(typeof(Xamarin.Forms.FlowDirection))</AttributeName>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
<Members>
|
||||
<Member MemberName=".ctor">
|
||||
<MemberSignature Language="C#" Value="public FlowDirectionConverter ();" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public hidebysig specialname rtspecialname instance void .ctor() cil managed" />
|
||||
<MemberType>Constructor</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Parameters />
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="ConvertFromInvariantString">
|
||||
<MemberSignature Language="C#" Value="public override object ConvertFromInvariantString (string value);" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public hidebysig virtual instance object ConvertFromInvariantString(string value) cil managed" />
|
||||
<MemberType>Method</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>System.Object</ReturnType>
|
||||
</ReturnValue>
|
||||
<Parameters>
|
||||
<Parameter Name="value" Type="System.String" />
|
||||
</Parameters>
|
||||
<Docs>
|
||||
<param name="value">To be added.</param>
|
||||
<summary>To be added.</summary>
|
||||
<returns>To be added.</returns>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
</Members>
|
||||
</Type>
|
|
@ -91,6 +91,37 @@ MainPage = new ContentPage () {
|
|||
<remarks>A Frame has a default <see cref="P:Xamarin.Forms.Layout.Padding" /> of 20.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="BorderColor">
|
||||
<MemberSignature Language="C#" Value="public Xamarin.Forms.Color BorderColor { get; set; }" />
|
||||
<MemberSignature Language="ILAsm" Value=".property instance valuetype Xamarin.Forms.Color BorderColor" />
|
||||
<MemberType>Property</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>Xamarin.Forms.Color</ReturnType>
|
||||
</ReturnValue>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<value>To be added.</value>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="BorderColorProperty">
|
||||
<MemberSignature Language="C#" Value="public static readonly Xamarin.Forms.BindableProperty BorderColorProperty;" />
|
||||
<MemberSignature Language="ILAsm" Value=".field public static initonly class Xamarin.Forms.BindableProperty BorderColorProperty" />
|
||||
<MemberType>Field</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>Xamarin.Forms.BindableProperty</ReturnType>
|
||||
</ReturnValue>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="CornerRadius">
|
||||
<MemberSignature Language="C#" Value="public float CornerRadius { get; set; }" />
|
||||
<MemberSignature Language="ILAsm" Value=".property instance float32 CornerRadius" />
|
||||
|
@ -205,6 +236,11 @@ MainPage = new ContentPage () {
|
|||
<AssemblyVersion>1.5.0.0</AssemblyVersion>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<AttributeName>System.Obsolete("OutlineColor is obsolete as of version 3.0.0. Please use BorderColor instead.")</AttributeName>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
<ReturnValue>
|
||||
<ReturnType>Xamarin.Forms.Color</ReturnType>
|
||||
</ReturnValue>
|
||||
|
@ -228,6 +264,11 @@ MainPage = new ContentPage () {
|
|||
<AssemblyVersion>1.5.0.0</AssemblyVersion>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<AttributeName>System.Obsolete("OutlineColorProperty is obsolete as of version 3.0.0. Please use BorderColorProperty instead.")</AttributeName>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
<ReturnValue>
|
||||
<ReturnType>Xamarin.Forms.BindableProperty</ReturnType>
|
||||
</ReturnValue>
|
||||
|
|
|
@ -97,6 +97,25 @@
|
|||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="Add">
|
||||
<MemberSignature Language="C#" Value="public void Add (Xamarin.Forms.StyleSheets.StyleSheet styleSheet);" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public hidebysig instance void Add(class Xamarin.Forms.StyleSheets.StyleSheet styleSheet) cil managed" />
|
||||
<MemberType>Method</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>System.Void</ReturnType>
|
||||
</ReturnValue>
|
||||
<Parameters>
|
||||
<Parameter Name="styleSheet" Type="Xamarin.Forms.StyleSheets.StyleSheet" />
|
||||
</Parameters>
|
||||
<Docs>
|
||||
<param name="styleSheet">To be added.</param>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="Add">
|
||||
<MemberSignature Language="C#" Value="public void Add (string key, object value);" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public hidebysig newslot virtual instance void Add(string key, object value) cil managed" />
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
<Base>
|
||||
<BaseTypeName>System.Enum</BaseTypeName>
|
||||
</Base>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<AttributeName>Xamarin.Forms.TypeConverter(typeof(Xamarin.Forms.TextAlignmentConverter))</AttributeName>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
<Docs>
|
||||
<summary>Enumerates values that control text alignment.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<Type Name="TextAlignmentConverter" FullName="Xamarin.Forms.TextAlignmentConverter">
|
||||
<TypeSignature Language="C#" Value="public class TextAlignmentConverter : Xamarin.Forms.TypeConverter" />
|
||||
<TypeSignature Language="ILAsm" Value=".class public auto ansi beforefieldinit TextAlignmentConverter extends Xamarin.Forms.TypeConverter" />
|
||||
<AssemblyInfo>
|
||||
<AssemblyName>Xamarin.Forms.Core</AssemblyName>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Base>
|
||||
<BaseTypeName>Xamarin.Forms.TypeConverter</BaseTypeName>
|
||||
</Base>
|
||||
<Interfaces />
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<AttributeName>Xamarin.Forms.Xaml.TypeConversion(typeof(Xamarin.Forms.TextAlignment))</AttributeName>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
<Members>
|
||||
<Member MemberName=".ctor">
|
||||
<MemberSignature Language="C#" Value="public TextAlignmentConverter ();" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public hidebysig specialname rtspecialname instance void .ctor() cil managed" />
|
||||
<MemberType>Constructor</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Parameters />
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="ConvertFromInvariantString">
|
||||
<MemberSignature Language="C#" Value="public override object ConvertFromInvariantString (string value);" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public hidebysig virtual instance object ConvertFromInvariantString(string value) cil managed" />
|
||||
<MemberType>Method</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>System.Object</ReturnType>
|
||||
</ReturnValue>
|
||||
<Parameters>
|
||||
<Parameter Name="value" Type="System.String" />
|
||||
</Parameters>
|
||||
<Docs>
|
||||
<param name="value">To be added.</param>
|
||||
<summary>To be added.</summary>
|
||||
<returns>To be added.</returns>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
</Members>
|
||||
</Type>
|
|
@ -0,0 +1,51 @@
|
|||
<Type Name="VisualElement+VisibilityConverter" FullName="Xamarin.Forms.VisualElement+VisibilityConverter">
|
||||
<TypeSignature Language="C#" Value="public class VisualElement.VisibilityConverter : Xamarin.Forms.TypeConverter" />
|
||||
<TypeSignature Language="ILAsm" Value=".class nested public auto ansi beforefieldinit VisualElement/VisibilityConverter extends Xamarin.Forms.TypeConverter" />
|
||||
<AssemblyInfo>
|
||||
<AssemblyName>Xamarin.Forms.Core</AssemblyName>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Base>
|
||||
<BaseTypeName>Xamarin.Forms.TypeConverter</BaseTypeName>
|
||||
</Base>
|
||||
<Interfaces />
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
<Members>
|
||||
<Member MemberName=".ctor">
|
||||
<MemberSignature Language="C#" Value="public VisibilityConverter ();" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public hidebysig specialname rtspecialname instance void .ctor() cil managed" />
|
||||
<MemberType>Constructor</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Parameters />
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="ConvertFromInvariantString">
|
||||
<MemberSignature Language="C#" Value="public override object ConvertFromInvariantString (string value);" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public hidebysig virtual instance object ConvertFromInvariantString(string value) cil managed" />
|
||||
<MemberType>Method</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>System.Object</ReturnType>
|
||||
</ReturnValue>
|
||||
<Parameters>
|
||||
<Parameter Name="value" Type="System.String" />
|
||||
</Parameters>
|
||||
<Docs>
|
||||
<param name="value">To be added.</param>
|
||||
<summary>To be added.</summary>
|
||||
<returns>To be added.</returns>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
</Members>
|
||||
</Type>
|
|
@ -880,6 +880,11 @@
|
|||
<AssemblyVersion>1.5.0.0</AssemblyVersion>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<AttributeName>Xamarin.Forms.TypeConverter(typeof(Xamarin.Forms.VisualElement/VisibilityConverter))</AttributeName>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
<ReturnValue>
|
||||
<ReturnType>System.Boolean</ReturnType>
|
||||
</ReturnValue>
|
||||
|
|
|
@ -213,6 +213,7 @@
|
|||
<Type Name="FileImageSource" Kind="Class" />
|
||||
<Type Name="FileImageSourceConverter" Kind="Class" />
|
||||
<Type Name="FlowDirection" Kind="Enumeration" />
|
||||
<Type Name="FlowDirectionConverter" Kind="Class" />
|
||||
<Type Name="FocusEventArgs" Kind="Class" />
|
||||
<Type Name="Font" Kind="Structure" />
|
||||
<Type Name="FontAttributes" Kind="Enumeration" />
|
||||
|
@ -397,6 +398,7 @@
|
|||
<Type Name="TemplatedView" Kind="Class" />
|
||||
<Type Name="TemplateExtensions" Kind="Class" />
|
||||
<Type Name="TextAlignment" Kind="Enumeration" />
|
||||
<Type Name="TextAlignmentConverter" Kind="Class" />
|
||||
<Type Name="TextCell" Kind="Class" />
|
||||
<Type Name="TextChangedEventArgs" Kind="Class" />
|
||||
<Type Name="Thickness" Kind="Structure" />
|
||||
|
@ -424,6 +426,7 @@
|
|||
<Type Name="ViewState" Kind="Enumeration" />
|
||||
<Type Name="VisualElement" Kind="Class" />
|
||||
<Type Name="VisualElement+FocusRequestArgs" Kind="Class" />
|
||||
<Type Name="VisualElement+VisibilityConverter" Kind="Class" />
|
||||
<Type Name="WebNavigatedEventArgs" Kind="Class" />
|
||||
<Type Name="WebNavigatingEventArgs" Kind="Class" />
|
||||
<Type Name="WebNavigationEvent" Kind="Enumeration" />
|
||||
|
@ -555,6 +558,9 @@
|
|||
<Type Name="Page" Kind="Class" />
|
||||
<Type Name="ToolbarPlacement" Kind="Enumeration" />
|
||||
</Namespace>
|
||||
<Namespace Name="Xamarin.Forms.StyleSheets">
|
||||
<Type Name="StyleSheet" Kind="Class" />
|
||||
</Namespace>
|
||||
<Namespace Name="Xamarin.Forms.Xaml">
|
||||
<Type Name="AcceptEmptyServiceProviderAttribute" Kind="Class" />
|
||||
<Type Name="IMarkupExtension" Kind="Interface" />
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<Namespace Name="Xamarin.Forms.StyleSheets">
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Namespace>
|
|
@ -0,0 +1,95 @@
|
|||
<Type Name="StyleSheetExtension" FullName="Xamarin.Forms.Xaml.StyleSheetExtension">
|
||||
<TypeSignature Language="C#" Value="public sealed class StyleSheetExtension : Xamarin.Forms.Xaml.IValueProvider" />
|
||||
<TypeSignature Language="ILAsm" Value=".class public auto ansi sealed beforefieldinit StyleSheetExtension extends System.Object implements class Xamarin.Forms.Xaml.IValueProvider" />
|
||||
<AssemblyInfo>
|
||||
<AssemblyName>Xamarin.Forms.Xaml</AssemblyName>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Base>
|
||||
<BaseTypeName>System.Object</BaseTypeName>
|
||||
</Base>
|
||||
<Interfaces>
|
||||
<Interface>
|
||||
<InterfaceName>Xamarin.Forms.Xaml.IValueProvider</InterfaceName>
|
||||
</Interface>
|
||||
</Interfaces>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<AttributeName>Xamarin.Forms.ContentProperty("Style")</AttributeName>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<AttributeName>Xamarin.Forms.Xaml.ProvideCompiled("Xamarin.Forms.Core.XamlC.StyleSheetProvider")</AttributeName>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
<Members>
|
||||
<Member MemberName=".ctor">
|
||||
<MemberSignature Language="C#" Value="public StyleSheetExtension ();" />
|
||||
<MemberSignature Language="ILAsm" Value=".method public hidebysig specialname rtspecialname instance void .ctor() cil managed" />
|
||||
<MemberType>Constructor</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Parameters />
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="Source">
|
||||
<MemberSignature Language="C#" Value="public Uri Source { get; set; }" />
|
||||
<MemberSignature Language="ILAsm" Value=".property instance class System.Uri Source" />
|
||||
<MemberType>Property</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>System.Uri</ReturnType>
|
||||
</ReturnValue>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<value>To be added.</value>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="Style">
|
||||
<MemberSignature Language="C#" Value="public string Style { get; set; }" />
|
||||
<MemberSignature Language="ILAsm" Value=".property instance string Style" />
|
||||
<MemberType>Property</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>System.String</ReturnType>
|
||||
</ReturnValue>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<value>To be added.</value>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
<Member MemberName="Xamarin.Forms.Xaml.IValueProvider.ProvideValue">
|
||||
<MemberSignature Language="C#" Value="object IValueProvider.ProvideValue (IServiceProvider serviceProvider);" />
|
||||
<MemberSignature Language="ILAsm" Value=".method hidebysig newslot virtual instance object Xamarin.Forms.Xaml.IValueProvider.ProvideValue(class System.IServiceProvider serviceProvider) cil managed" />
|
||||
<MemberType>Method</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>System.Object</ReturnType>
|
||||
</ReturnValue>
|
||||
<Parameters>
|
||||
<Parameter Name="serviceProvider" Type="System.IServiceProvider" />
|
||||
</Parameters>
|
||||
<Docs>
|
||||
<param name="serviceProvider">To be added.</param>
|
||||
<summary>To be added.</summary>
|
||||
<returns>To be added.</returns>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
</Members>
|
||||
</Type>
|
|
@ -80,6 +80,7 @@
|
|||
<Type Name="ReferenceExtension" Kind="Class" />
|
||||
<Type Name="StaticExtension" Kind="Class" />
|
||||
<Type Name="StaticResourceExtension" Kind="Class" />
|
||||
<Type Name="StyleSheetExtension" Kind="Class" />
|
||||
<Type Name="TemplateBindingExtension" Kind="Class" />
|
||||
<Type Name="TypeExtension" Kind="Class" />
|
||||
<Type Name="XamlCompilationAttribute" Kind="Class" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче