From b5ac723a37f93107fb9737a947336692955e0942 Mon Sep 17 00:00:00 2001 From: endlesstravel Date: Tue, 26 Jan 2021 08:33:16 +0800 Subject: [PATCH 1/2] fix bug like {c:Binding .} --- CalcBinding/PathAnalysis/PropertyPathAnalyzer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CalcBinding/PathAnalysis/PropertyPathAnalyzer.cs b/CalcBinding/PathAnalysis/PropertyPathAnalyzer.cs index 54edd83..9435c1f 100644 --- a/CalcBinding/PathAnalysis/PropertyPathAnalyzer.cs +++ b/CalcBinding/PathAnalysis/PropertyPathAnalyzer.cs @@ -189,6 +189,12 @@ private bool GetPath(Chunk chunk, out PathToken pathToken) private bool GetPropChain(string str, out List propChain) { + if (str.Trim() == ".") + { + propChain = new List() { "." }; + return true; + } + var properties = str.Split(new[] { '.' }, StringSplitOptions.None); if (properties.All(IsIdentifier) && properties.Any()) From 85ca73b5d749bd848280574a0a38bebcb7892080 Mon Sep 17 00:00:00 2001 From: endlesstravel Date: Tue, 26 Jan 2021 15:11:04 +0800 Subject: [PATCH 2/2] add support for shortcut of RelativeSource --- CalcBinding/CalcBinding.cs | 67 +++++++++++++++++-- .../PathAnalysis/PropertyPathAnalyzer.cs | 28 +++++++- .../Tokens/Abstract/Help/PathTokenId.cs | 1 + README.md | 48 +++++++++++++ 4 files changed, 136 insertions(+), 8 deletions(-) diff --git a/CalcBinding/CalcBinding.cs b/CalcBinding/CalcBinding.cs index 7c82cdf..b7b79e7 100644 --- a/CalcBinding/CalcBinding.cs +++ b/CalcBinding/CalcBinding.cs @@ -54,6 +54,57 @@ public Binding(String path) Path = path; } + static Regex RelativeSourceDefRegex = new Regex(@"(PreviousData|TemplatedParent|Self)|((FindAncestor\.)?(\[(\d+)\]\.)?(.+))", RegexOptions.Compiled); + void AdjustRelativeSourceDef(IServiceProvider serviceProvider, System.Windows.Data.Binding binding, string relativeSourceDef) + { + if (relativeSourceDef != null) + { + var m = RelativeSourceDefRegex.Match(relativeSourceDef); + if (!m.Success) + throw new Exception(@"not exception input of RelativeSource define, it must be +zzzzz@PreviousData // means {Binding Path=zzzzz, RelativeSource={RelativeSource PreviousData}} +yyyyy@TemplatedParent // means {Binding Path=yyyyy, RelativeSource={RelativeSource TemplatedParent}} +Parent.Name@Self // means {Binding Path=Parent.Name, RelativeSource={RelativeSource Self}} +xxxx@FindAncestor.Grid // means {Binding Path=xxxx, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}} +xxxx@FindAncestor[2].Grid // means {Binding Path=xxxx, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}, AncestorLevel=2}} +xxxx@Grid // means {Binding Path=xxxx, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}} +Title@Window // means {Binding Path=Title, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}} +"); + + if (m.Groups[1].Success) + { + if (Enum.TryParse(relativeSourceDef, out RelativeSourceMode modeObj)) + { + binding.RelativeSource = new RelativeSource() + { + Mode = modeObj, + }; + } + } + else if (m.Groups[2].Success) + { + binding.RelativeSource = new RelativeSource() + { + Mode = RelativeSourceMode.FindAncestor, + AncestorLevel = m.Groups[5].Success ? int.Parse(m.Groups[5].Value) : 1, + AncestorType = new TypeExtension(m.Groups[6].Value).ProvideValue(serviceProvider) as Type, + }; + } + else + { + throw new Exception($"Wrong grammar on '{relativeSourceDef}'"); + } + + } + else + { + if (RelativeSource != null) + { + binding.RelativeSource = RelativeSource; + } + } + } + public override object ProvideValue(IServiceProvider serviceProvider) { var targetPropertyType = GetPropertyType(serviceProvider); @@ -77,6 +128,8 @@ public override object ProvideValue(IServiceProvider serviceProvider) BindingBase resBinding; + + if (bindingPathes.Count == 1) { // todo: can enums be binded ? What if one value is Enum? bug.. @@ -99,6 +152,7 @@ public override object ProvideValue(IServiceProvider serviceProvider) var pathId = bindingPathes.Single().PathId; // we need to use convert from string for support of static properties var pathValue = pathId.Value; + AdjustRelativeSourceDef(serviceProvider, binding, pathId.RelativeSourceDef); if (pathId.PathType == PathTokenType.StaticProperty) { @@ -113,8 +167,8 @@ public override object ProvideValue(IServiceProvider serviceProvider) if (ElementName != null) binding.ElementName = ElementName; - if (RelativeSource != null) - binding.RelativeSource = RelativeSource; + //if (RelativeSource != null) + // binding.RelativeSource = RelativeSource; if (StringFormat != null) binding.StringFormat = StringFormat; @@ -162,6 +216,8 @@ public override object ProvideValue(IServiceProvider serviceProvider) // we need to use convert from string for support of static properties var pathValue = path.PathId.Value; + AdjustRelativeSourceDef(serviceProvider, binding, path.PathId.RelativeSourceDef); + if (path.PathId.PathType == PathTokenType.StaticProperty) { pathValue = string.Format("({0})", pathValue); // need to use brackets for Static property recognition in standart binding @@ -177,8 +233,8 @@ public override object ProvideValue(IServiceProvider serviceProvider) if (ElementName != null) binding.ElementName = ElementName; - if (RelativeSource != null) - binding.RelativeSource = RelativeSource; + //if (RelativeSource != null) + // binding.RelativeSource = RelativeSource; mBinding.Bindings.Add(binding); } @@ -239,7 +295,7 @@ private string GetExpressionTemplate(string path, List properti if (targetProp != null) { - var propPath = propId.Value; + var propPath = propId.RelativeSourceDef == null ? propId.Value : $"{propId.Value}@{propId.RelativeSourceDef}"; if (propId.PathType == PathTokenType.Property || propId.PathType == PathTokenType.StaticProperty) { @@ -512,6 +568,7 @@ private string NormalizePath(string path) // of the binding source to use. The default is null. [DefaultValue("")] public RelativeSource RelativeSource { get; set; } + // // Summary: // Gets or sets the object to use as the binding source. diff --git a/CalcBinding/PathAnalysis/PropertyPathAnalyzer.cs b/CalcBinding/PathAnalysis/PropertyPathAnalyzer.cs index 9435c1f..6b44fd3 100644 --- a/CalcBinding/PathAnalysis/PropertyPathAnalyzer.cs +++ b/CalcBinding/PathAnalysis/PropertyPathAnalyzer.cs @@ -154,6 +154,28 @@ private bool GetPath(Chunk chunk, out PathToken pathToken) return false; } + // error check + var spiltByTypeDefArray = str.Split(new char[] {'@' }); + string relativeSourceDef = null; + if (spiltByTypeDefArray.Length == 1) + { + + } + else if (spiltByTypeDefArray.Length == 2) + { + if (spiltByTypeDefArray[0].Length == 0 || spiltByTypeDefArray[1].Length == 0) + throw new Exception($"Wrong grammar on '{str}'"); + + str = spiltByTypeDefArray[0]; + relativeSourceDef = spiltByTypeDefArray[1]; + } + else if (spiltByTypeDefArray.Length > 2) + { + throw new Exception($"Wrong grammar on '{str}'"); + } + // no error + + var colonPos = str.IndexOf(':'); if (colonPos > 0) @@ -178,7 +200,7 @@ private bool GetPath(Chunk chunk, out PathToken pathToken) List propChain; if (GetPropChain(str, out propChain)) { - pathToken = GetPropPathOrMath(chunk, propChain); + pathToken = GetPropPathOrMath(chunk, propChain, relativeSourceDef); return true; } } @@ -224,7 +246,7 @@ private bool IsIdentifier(string str) return true; } - private PathToken GetPropPathOrMath(Chunk chunk, List propChain) + private PathToken GetPropPathOrMath(Chunk chunk, List propChain, string relativeSourceDef) { PathToken pathToken = null; @@ -236,7 +258,7 @@ private PathToken GetPropPathOrMath(Chunk chunk, List propChain) { pathToken = new PropertyPathToken(chunk.Start, chunk.End, propChain); } - + pathToken.Id.RelativeSourceDef = relativeSourceDef; return pathToken; } diff --git a/CalcBinding/PathAnalysis/Tokens/Abstract/Help/PathTokenId.cs b/CalcBinding/PathAnalysis/Tokens/Abstract/Help/PathTokenId.cs index 7cc8387..97ff24b 100644 --- a/CalcBinding/PathAnalysis/Tokens/Abstract/Help/PathTokenId.cs +++ b/CalcBinding/PathAnalysis/Tokens/Abstract/Help/PathTokenId.cs @@ -10,6 +10,7 @@ public class PathTokenId { public PathTokenType PathType { get; private set; } public string Value { get; private set; } + public string RelativeSourceDef{ get; set; } public PathTokenId(PathTokenType pathType, string value) { diff --git a/README.md b/README.md index 466cad1..ac007dd 100644 --- a/README.md +++ b/README.md @@ -407,6 +407,54 @@ Setting RelativeSource property to TemplatedParent value makes CalcBinding simil 3. In path expression you can't use any methods of .Net classes except of Math class. +## 10. Shortcut of use RelativeSource in binding +Using RelativeSource property is easy in path, just use by like below: +`PreviousData` , `FindAncestor` , `Self`, `TemplatedParent` + + 1. all example +``` +zzzzz@PreviousData // means {Binding Path=zzzzz, RelativeSource={RelativeSource PreviousData}} +yyyyy@TemplatedParent // means {Binding Path=yyyyy, RelativeSource={RelativeSource TemplatedParent}} +Parent.Name@Self // means {Binding Path=Parent.Name, RelativeSource={RelativeSource Self}} +xxxx@FindAncestor.Grid // means {Binding Path=xxxx, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}} +xxxx@FindAncestor[2].Grid // means {Binding Path=xxxx, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}, AncestorLevel=2}} +xxxx@Grid // means {Binding Path=xxxx, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}} +Title@Window // means {Binding Path=Title, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}} +``` + +2. set width by 50% width and 50% height of parent +```xml + + + +``` + +3. use DataContext for window by FindAncestor +```C# + this.DataContext = new TaskBox() { + TaskList = new string[]{"task a", "task b", "task c"}, + PageIndex = "task a", + }; +``` +```xml + + + + + + + + + + + +``` + + ## What is inside? CalcBinding uses DynamicExpresso library to parse string expression to Linq Expression and compiled expression tree for binding.