Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 62 additions & 5 deletions CalcBinding/CalcBinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<RelativeSourceMode>(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);
Expand All @@ -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..
Expand All @@ -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)
{
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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);
}
Expand Down Expand Up @@ -239,7 +295,7 @@ private string GetExpressionTemplate(string path, List<PathAppearances> 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)
{
Expand Down Expand Up @@ -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.
Expand Down
34 changes: 31 additions & 3 deletions CalcBinding/PathAnalysis/PropertyPathAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -178,7 +200,7 @@ private bool GetPath(Chunk chunk, out PathToken pathToken)
List<string> propChain;
if (GetPropChain(str, out propChain))
{
pathToken = GetPropPathOrMath(chunk, propChain);
pathToken = GetPropPathOrMath(chunk, propChain, relativeSourceDef);
return true;
}
}
Expand All @@ -189,6 +211,12 @@ private bool GetPath(Chunk chunk, out PathToken pathToken)

private bool GetPropChain(string str, out List<string> propChain)
{
if (str.Trim() == ".")
{
propChain = new List<string>() { "." };
return true;
}

var properties = str.Split(new[] { '.' }, StringSplitOptions.None);

if (properties.All(IsIdentifier) && properties.Any())
Expand Down Expand Up @@ -218,7 +246,7 @@ private bool IsIdentifier(string str)
return true;
}

private PathToken GetPropPathOrMath(Chunk chunk, List<string> propChain)
private PathToken GetPropPathOrMath(Chunk chunk, List<string> propChain, string relativeSourceDef)
{
PathToken pathToken = null;

Expand All @@ -230,7 +258,7 @@ private PathToken GetPropPathOrMath(Chunk chunk, List<string> propChain)
{
pathToken = new PropertyPathToken(chunk.Start, chunk.End, propChain);
}

pathToken.Id.RelativeSourceDef = relativeSourceDef;
return pathToken;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<Canvas Width="1200" Height="200">
<Button Width="{c:Binding '(0.5 * Parent.ActualWidth@Self)'}" Height="{c:Binding '(0.5 * Parent.ActualHeight@Self)'}">button</Button>
</Canvas>
```

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
<Window ....>
<!-- .... -->
<ListBox ItemsSource="{Binding TaskList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton Content="{c:Binding .}"
IsChecked="{c:Binding 'DataContext.PageIndex@Window==.'}"
Command="{c:Binding 'DataContext.ChangePageIndexCommad@Window'}"
CommandParameter="{c:Binding .}"
/>

</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
```


## What is inside?

CalcBinding uses DynamicExpresso library to parse string expression to Linq Expression and compiled expression tree for binding.
Expand Down