Да, к сожалению, расширение разметки RelativeSource все еще в некотором роде повреждено в Silverlight ... все, что он поддерживает, это TemplatedParent и Self, если память служит. А поскольку Silverlight не поддерживает созданные пользователем расширения разметки (пока), прямого аналога синтаксиса FindAncestor нет.
Теперь, понимая, что это был бесполезный комментарий, давайте посмотрим, сможем ли мы найти другой способ сделать это. Я предполагаю, что проблема с прямым переносом синтаксиса FindAncestor из WPF в silverlight связана с тем фактом, что Silverlight не имеет истинного логического дерева. Интересно, можно ли использовать ValueConverter или Attached Behavior для создания аналога VisualTree-walking ...
(происходит поиск в Google)
Эй, похоже, кто-то еще пытался сделать это в Silverlight 2.0 для реализации ElementName - это может быть хорошим началом для обходного пути:
http://www.scottlogic.co.uk/blog/colin/2009/02/relativesource-binding-in-silverlight/
EDIT:
Хорошо, вот и все - нужно отдать должное автору вышеупомянутому автору, но я настроил его, чтобы удалить некоторые ошибки и т. Д. - все еще остается масса возможностей для улучшений:
public class BindingProperties
{
public string SourceProperty { get; set; }
public string ElementName { get; set; }
public string TargetProperty { get; set; }
public IValueConverter Converter { get; set; }
public object ConverterParameter { get; set; }
public bool RelativeSourceSelf { get; set; }
public BindingMode Mode { get; set; }
public string RelativeSourceAncestorType { get; set; }
public int RelativeSourceAncestorLevel { get; set; }
public BindingProperties()
{
RelativeSourceAncestorLevel = 1;
}
}
public static class BindingHelper
{
public class ValueObject : INotifyPropertyChanged
{
private object _value;
public object Value
{
get { return _value; }
set
{
_value = value;
OnPropertyChanged("Value");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public static BindingProperties GetBinding(DependencyObject obj)
{
return (BindingProperties)obj.GetValue(BindingProperty);
}
public static void SetBinding(DependencyObject obj, BindingProperties value)
{
obj.SetValue(BindingProperty, value);
}
public static readonly DependencyProperty BindingProperty =
DependencyProperty.RegisterAttached("Binding", typeof(BindingProperties), typeof(BindingHelper),
new PropertyMetadata(null, OnBinding));
/// <summary>
/// property change event handler for BindingProperty
/// </summary>
private static void OnBinding(
DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
FrameworkElement targetElement = depObj as FrameworkElement;
targetElement.Loaded += new RoutedEventHandler(TargetElement_Loaded);
}
private static void TargetElement_Loaded(object sender, RoutedEventArgs e)
{
FrameworkElement targetElement = sender as FrameworkElement;
// get the value of our attached property
BindingProperties bindingProperties = GetBinding(targetElement);
if (bindingProperties.ElementName != null)
{
// perform our 'ElementName' lookup
FrameworkElement sourceElement = targetElement.FindName(bindingProperties.ElementName) as FrameworkElement;
// bind them
CreateRelayBinding(targetElement, sourceElement, bindingProperties);
}
else if (bindingProperties.RelativeSourceSelf)
{
// bind an element to itself.
CreateRelayBinding(targetElement, targetElement, bindingProperties);
}
else if (!string.IsNullOrEmpty(bindingProperties.RelativeSourceAncestorType))
{
Type ancestorType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(
t => t.Name.Contains(bindingProperties.RelativeSourceAncestorType));
if(ancestorType == null)
{
ancestorType = Assembly.GetCallingAssembly().GetTypes().FirstOrDefault(
t => t.Name.Contains(bindingProperties.RelativeSourceAncestorType));
}
// navigate up the tree to find the type
DependencyObject currentObject = targetElement;
int currentLevel = 0;
while (currentLevel < bindingProperties.RelativeSourceAncestorLevel)
{
do
{
currentObject = VisualTreeHelper.GetParent(currentObject);
if(currentObject.GetType().IsSubclassOf(ancestorType))
{
break;
}
}
while (currentObject.GetType().Name != bindingProperties.RelativeSourceAncestorType);
currentLevel++;
}
FrameworkElement sourceElement = currentObject as FrameworkElement;
// bind them
CreateRelayBinding(targetElement, sourceElement, bindingProperties);
}
}
private static readonly BindingFlags dpFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
private struct RelayBindingKey
{
public DependencyProperty dependencyObject;
public FrameworkElement frameworkElement;
}
/// <summary>
/// A cache of relay bindings, keyed by RelayBindingKey which specifies a property of a specific
/// framework element.
/// </summary>
private static Dictionary<RelayBindingKey, ValueObject> relayBindings = new Dictionary<RelayBindingKey, ValueObject>();
/// <summary>
/// Creates a relay binding between the two given elements using the properties and converters
/// detailed in the supplied bindingProperties.
/// </summary>
private static void CreateRelayBinding(FrameworkElement targetElement, FrameworkElement sourceElement,
BindingProperties bindingProperties)
{
string sourcePropertyName = bindingProperties.SourceProperty + "Property";
string targetPropertyName = bindingProperties.TargetProperty + "Property";
// find the source dependency property
FieldInfo[] sourceFields = sourceElement.GetType().GetFields(dpFlags);
FieldInfo sourceDependencyPropertyField = sourceFields.First(i => i.Name == sourcePropertyName);
DependencyProperty sourceDependencyProperty = sourceDependencyPropertyField.GetValue(null) as DependencyProperty;
// find the target dependency property
FieldInfo[] targetFields = targetElement.GetType().GetFields(dpFlags);
FieldInfo targetDependencyPropertyField = targetFields.First(i => i.Name == targetPropertyName);
DependencyProperty targetDependencyProperty = targetDependencyPropertyField.GetValue(null) as DependencyProperty;
ValueObject relayObject;
bool relayObjectBoundToSource = false;
// create a key that identifies this source binding
RelayBindingKey key = new RelayBindingKey()
{
dependencyObject = sourceDependencyProperty,
frameworkElement = sourceElement
};
// do we already have a binding to this property?
if (relayBindings.ContainsKey(key))
{
relayObject = relayBindings[key];
relayObjectBoundToSource = true;
}
else
{
// create a relay binding between the two elements
relayObject = new ValueObject();
}
// initialise the relay object with the source dependency property value
relayObject.Value = sourceElement.GetValue(sourceDependencyProperty);
// create the binding for our target element to the relay object, this binding will
// include the value converter
Binding targetToRelay = new Binding();
targetToRelay.Source = relayObject;
targetToRelay.Path = new PropertyPath("Value");
targetToRelay.Mode = bindingProperties.Mode;
targetToRelay.Converter = bindingProperties.Converter;
targetToRelay.ConverterParameter = bindingProperties.ConverterParameter;
// set the binding on our target element
targetElement.SetBinding(targetDependencyProperty, targetToRelay);
if (!relayObjectBoundToSource && bindingProperties.Mode == BindingMode.TwoWay)
{
// create the binding for our source element to the relay object
Binding sourceToRelay = new Binding();
sourceToRelay.Source = relayObject;
sourceToRelay.Path = new PropertyPath("Value");
sourceToRelay.Converter = bindingProperties.Converter;
sourceToRelay.ConverterParameter = bindingProperties.ConverterParameter;
sourceToRelay.Mode = bindingProperties.Mode;
// set the binding on our source element
sourceElement.SetBinding(sourceDependencyProperty, sourceToRelay);
relayBindings.Add(key, relayObject);
}
}
}
Вы бы использовали это так:
<TextBlock>
<SilverlightApplication1:BindingHelper.Binding>
<SilverlightApplication1:BindingProperties
TargetProperty="Text"
SourceProperty="ActualWidth"
RelativeSourceAncestorType="UserControl"
Mode="OneWay"
/>
</SilverlightApplication1:BindingHelper.Binding>
</TextBlock>