Browse Source

Implement RelativeSource binding syntax sugar except for parsing AncestorType.

pull/1209/head
Jeremy Koritzinsky 9 years ago
parent
commit
12e32dc08e
  1. 96
      src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs

96
src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs

@ -96,20 +96,23 @@ namespace Avalonia.Markup.Xaml.Data
ValidateState(pathInfo);
enableDataValidation = enableDataValidation && Priority == BindingPriority.LocalValue;
var elementName = pathInfo.ElementName;
var relativeSource = RelativeSource ?? pathInfo.RelativeSource;
ExpressionObserver observer;
if (pathInfo.ElementName != null || ElementName != null)
if (elementName != null)
{
observer = CreateElementObserver(
(target as IControl) ?? (anchor as IControl),
pathInfo.ElementName ?? ElementName,
elementName,
pathInfo.Path);
}
else if (Source != null)
{
observer = CreateSourceObserver(Source, pathInfo.Path, enableDataValidation);
}
else if (RelativeSource == null || RelativeSource.Mode == RelativeSourceMode.DataContext)
else if (relativeSource == null || relativeSource.Mode == RelativeSourceMode.DataContext)
{
observer = CreateDataContexObserver(
target,
@ -118,23 +121,24 @@ namespace Avalonia.Markup.Xaml.Data
anchor,
enableDataValidation);
}
else if (RelativeSource.Mode == RelativeSourceMode.Self)
else if (relativeSource.Mode == RelativeSourceMode.Self)
{
observer = CreateSourceObserver(target, pathInfo.Path, enableDataValidation);
}
else if (RelativeSource.Mode == RelativeSourceMode.TemplatedParent)
else if (relativeSource.Mode == RelativeSourceMode.TemplatedParent)
{
observer = CreateTemplatedParentObserver(target, pathInfo.Path);
}
else if (RelativeSource.Mode == RelativeSourceMode.FindAncestor)
else if (relativeSource.Mode == RelativeSourceMode.FindAncestor)
{
if (RelativeSource.AncestorType == null)
if (relativeSource.AncestorType == null)
{
throw new InvalidOperationException("AncestorType must be set for RelativeSourceModel.FindAncestor.");
}
observer = CreateFindAncestorObserver(
(target as IControl) ?? (anchor as IControl),
relativeSource,
pathInfo.Path);
}
else
@ -187,6 +191,66 @@ namespace Avalonia.Markup.Xaml.Data
result.ElementName = path.Substring(1);
}
}
else if (path.StartsWith("$"))
{
var relativeSource = new RelativeSource();
result.RelativeSource = relativeSource;
var dot = path.IndexOf('.');
string relativeSourceMode;
if (dot != -1)
{
result.Path = path.Substring(dot + 1);
relativeSourceMode = path.Substring(1, dot - 1);
}
else
{
result.Path = string.Empty;
relativeSourceMode = path.Substring(1);
}
if (relativeSourceMode == "self")
{
relativeSource.Mode = RelativeSourceMode.Self;
}
else if(relativeSourceMode == "parent")
{
relativeSource.Mode = RelativeSourceMode.FindAncestor;
var parentConfigStart = relativeSourceMode.IndexOf('[');
if (parentConfigStart != -1)
{
if (!relativeSourceMode.EndsWith("]"))
{
throw new InvalidOperationException("Invalid RelativeSource binding syntax. Expected matching ']' for '['.");
}
var parentConfigParams = relativeSourceMode.Substring(0, relativeSourceMode.Length - 1).Split(',');
if (parentConfigParams.Length > 2 || parentConfigParams.Length == 0)
{
throw new InvalidOperationException("Expected either 1 or 2 parameters for RelativeSource binding syntax");
}
else if (parentConfigParams.Length == 1)
{
if (int.TryParse(parentConfigParams[0], out int level))
{
relativeSource.AncestorType = typeof(IControl);
relativeSource.AncestorLevel = level;
}
else
{
relativeSource.AncestorType = LookupAncestorType(parentConfigParams[0]);
}
}
else
{
relativeSource.AncestorType = LookupAncestorType(parentConfigParams[0]);
relativeSource.AncestorLevel = int.Parse(parentConfigParams[1]);
}
}
}
else
{
throw new InvalidOperationException($"Invalid RelativeSource binding syntax: {relativeSourceMode}");
}
}
else
{
result.Path = path;
@ -195,6 +259,12 @@ namespace Avalonia.Markup.Xaml.Data
return result;
}
private static Type LookupAncestorType(string ancestorTypeName)
{
//TODO: What is our syntax for type lookup here?
throw new NotImplementedException();
}
private void ValidateState(PathInfo pathInfo)
{
if (pathInfo.ElementName != null && ElementName != null)
@ -203,8 +273,14 @@ namespace Avalonia.Markup.Xaml.Data
"ElementName property cannot be set when an #elementName path is provided.");
}
if (pathInfo.RelativeSource != null && RelativeSource != null)
{
throw new InvalidOperationException(
"ElementName property cannot be set when a $self or $parent path is provided.");
}
if ((pathInfo.ElementName != null || ElementName != null) &&
RelativeSource != null)
(pathInfo.RelativeSource != null || RelativeSource != null))
{
throw new InvalidOperationException(
"ElementName property cannot be set with a RelativeSource.");
@ -267,12 +343,13 @@ namespace Avalonia.Markup.Xaml.Data
private ExpressionObserver CreateFindAncestorObserver(
IControl target,
RelativeSource relativeSource,
string path)
{
Contract.Requires<ArgumentNullException>(target != null);
return new ExpressionObserver(
ControlLocator.Track(target, RelativeSource.AncestorType, RelativeSource.AncestorLevel -1),
ControlLocator.Track(target, relativeSource.AncestorType, relativeSource.AncestorLevel -1),
path);
}
@ -324,6 +401,7 @@ namespace Avalonia.Markup.Xaml.Data
{
public string Path { get; set; }
public string ElementName { get; set; }
public RelativeSource RelativeSource { get; set; }
}
}
}
Loading…
Cancel
Save