原文:
RelativeSource 简述
RelativeSource实现标记扩展,以描述绑定源相对于绑定目标的位置。
<Binding> <Binding.RelativeSource> <RelativeSource Mode="modeEnumValue"/> </Binding.RelativeSource> </Binding> - or <Binding> <Binding.RelativeSource> <RelativeSource Mode="FindAncestor" AncestorType="{x:Type typeName}" AncestorLevel="intLevel" /> </Binding.RelativeSource> </Binding>
// Summary: // Describes the location of the binding source relative to the position of // the binding target. public enum RelativeSourceMode { // Summary: // Allows you to bind the previous data item (not that control that contains // the data item) in the list of data items being displayed. PreviousData = 0, // // Summary: // Refers to the element to which the template (in which the data-bound element // exists) is applied. This is similar to setting a System.Windows.TemplateBindingExtension // and is only applicable if the System.Windows.Data.Binding is within a template. TemplatedParent = 1, // // Summary: // Refers to the element on which you are setting the binding and allows you // to bind one property of that element to another property on the same element. Self = 2, // // Summary: // Refers to the ancestor in the parent chain of the data-bound element. You // can use this to bind to an ancestor of a specific type or its subclasses. // This is the mode you use if you want to specify System.Windows.Data.RelativeSource.AncestorType // and/or System.Windows.Data.RelativeSource.AncestorLevel. FindAncestor = 3, }
Xaml 示例
<Window.Resources> <ControlTemplate x:Key="template"> <Canvas> <Canvas.RenderTransform> <RotateTransform Angle="20"/> </Canvas.RenderTransform> <Ellipse Height="100" Width="150" Fill="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Background}"></Ellipse> <ContentPresenter Margin="35" Content="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Content}"/> </Canvas> </ControlTemplate> </Window.Resources> <StackPanel> <TextBlock> <TextBlock.Text> <Binding Path="Title"> <Binding.RelativeSource> <RelativeSource Mode="FindAncestor" AncestorType="{x:Type Window}" /> </Binding.RelativeSource> </Binding> </TextBlock.Text> </TextBlock> <TextBlock Text="{Binding Path=Title,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}} }"></TextBlock>
<Button Template="{StaticResource template}" Background="AliceBlue"> <TextBlock FontSize="22">Click me</TextBlock> </Button> </StackPanel>
RelativeSource内部实现
using System; using System.ComponentModel; using System.Windows.Markup; namespace System.Windows.Data { public class RelativeSource : MarkupExtension, ISupportInitialize { public RelativeSource(); public RelativeSource(RelativeSourceMode mode); public RelativeSource(RelativeSourceMode mode, Type ancestorType, int ancestorLevel); public Type AncestorType { get; set; }
public RelativeSourceMode Mode { get; set; } public static RelativeSource PreviousData { get; } public static RelativeSource Self { get; } public static RelativeSource TemplatedParent { get; } public override object ProvideValue(IServiceProvider serviceProvider); ... } }
using System; using System.ComponentModel; using System.Windows.Markup;
namespace System.Windows.Data { /// <summary>Implements a markup extension that describes the location of the binding source relative to the position of the binding target.</summary> [MarkupExtensionReturnType(typeof(RelativeSource))] public class RelativeSource : MarkupExtension, ISupportInitialize { private RelativeSourceMode _mode; private Type _ancestorType; private int _ancestorLevel = -1; private static RelativeSource s_previousData; private static RelativeSource s_templatedParent; private static RelativeSource s_self; /// <summary>Gets a static value that is used to return a <see cref="T:System.Windows.Data.RelativeSource" /> constructed for the <see cref="F:System.Windows.Data.RelativeSourceMode.PreviousData" /> mode.</summary> /// <returns>A static <see cref="T:System.Windows.Data.RelativeSource" />.</returns> public static RelativeSource PreviousData { get { if (RelativeSource.s_previousData == null) { RelativeSource.s_previousData = new RelativeSource(RelativeSourceMode.PreviousData); } return RelativeSource.s_previousData; } } /// <summary>Gets a static value that is used to return a <see cref="T:System.Windows.Data.RelativeSource" /> constructed for the <see cref="F:System.Windows.Data.RelativeSourceMode.TemplatedParent" /> mode.</summary> /// <returns>A static <see cref="T:System.Windows.Data.RelativeSource" />.</returns> public static RelativeSource TemplatedParent { get { if (RelativeSource.s_templatedParent == null) { RelativeSource.s_templatedParent = new RelativeSource(RelativeSourceMode.TemplatedParent); } return RelativeSource.s_templatedParent; } } /// <summary>Gets a static value that is used to return a <see cref="T:System.Windows.Data.RelativeSource" /> constructed for the <see cref="F:System.Windows.Data.RelativeSourceMode.Self" /> mode.</summary> /// <returns>A static <see cref="T:System.Windows.Data.RelativeSource" />.</returns> public static RelativeSource Self { get { if (RelativeSource.s_self == null) { RelativeSource.s_self = new RelativeSource(RelativeSourceMode.Self); } return RelativeSource.s_self; } } /// <summary>Gets or sets a <see cref="T:System.Windows.Data.RelativeSourceMode" /> value that describes the location of the binding source relative to the position of the binding target.</summary> /// <returns>One of the <see cref="T:System.Windows.Data.RelativeSourceMode" /> values. The default value is null.</returns> /// <exception cref="T:System.InvalidOperationException">This property is immutable after initialization. Instead of changing the <see cref="P:System.Windows.Data.RelativeSource.Mode" /> on this instance, create a new <see cref="T:System.Windows.Data.RelativeSource" /> or use a different static instance.</exception> [ConstructorArgument("mode")] public RelativeSourceMode Mode { get { return this._mode; } set { if (this.IsUninitialized) { this.InitializeMode(value); return; } if (value != this._mode) { throw new InvalidOperationException(SR.Get("RelativeSourceModeIsImmutable")); } } } /// <summary>Gets or sets the type of ancestor to look for.</summary> /// <returns>The type of ancestor. The default value is null.</returns> /// <exception cref="T:System.InvalidOperationException">The <see cref="T:System.Windows.Data.RelativeSource" /> is not in the <see cref="F:System.Windows.Data.RelativeSourceMode.FindAncestor" /> mode.</exception> public Type AncestorType { get { return this._ancestorType; } set { if (this.IsUninitialized) { this.AncestorLevel = 1; } if (this._mode != RelativeSourceMode.FindAncestor) { if (value != null) { throw new InvalidOperationException(SR.Get("RelativeSourceNotInFindAncestorMode")); } } else { this._ancestorType = value; } } } /// <summary>Gets or sets the level of ancestor to look for, in <see cref="F:System.Windows.Data.RelativeSourceMode.FindAncestor" /> mode. Use 1 to indicate the one nearest to the binding target element.</summary> /// <returns>The ancestor level. Use 1 to indicate the one nearest to the binding target element.</returns> public int AncestorLevel { get { return this._ancestorLevel; } set { if (this._mode != RelativeSourceMode.FindAncestor) { if (value != 0) { throw new InvalidOperationException(SR.Get("RelativeSourceNotInFindAncestorMode")); } } else { if (value < 1) { throw new ArgumentOutOfRangeException(SR.Get("RelativeSourceInvalidAncestorLevel")); } this._ancestorLevel = value; } } } private bool IsUninitialized { get { return this._ancestorLevel == -1; } } /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Data.RelativeSource" /> class.</summary> public RelativeSource() { this._mode = RelativeSourceMode.FindAncestor; } /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Data.RelativeSource" /> class with an initial mode.</summary> /// <param name="mode">One of the <see cref="T:System.Windows.Data.RelativeSourceMode" /> values.</param> public RelativeSource(RelativeSourceMode mode) { this.InitializeMode(mode); } /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Data.RelativeSource" /> class with an initial mode and additional tree-walking qualifiers for finding the desired relative source.</summary> /// <param name="mode">One of the <see cref="T:System.Windows.Data.RelativeSourceMode" /> values. For this signature to be relevant, this should be <see cref="F:System.Windows.Data.RelativeSourceMode.FindAncestor" />.</param> /// <param name="ancestorType">The <see cref="T:System.Type" /> of ancestor to look for.</param> /// <param name="ancestorLevel">The ordinal position of the desired ancestor among all ancestors of the given type. </param> public RelativeSource(RelativeSourceMode mode, Type ancestorType, int ancestorLevel) { this.InitializeMode(mode); this.AncestorType = ancestorType; this.AncestorLevel = ancestorLevel; } /// <summary>This member supports the Windows Presentation Foundation (WPF) infrastructure and is not intended to be used directly from your code.</summary> void ISupportInitialize.BeginInit() { }
///<summary>This member supports the Windows Presentation Foundation (WPF) infrastructure and is not intended to be used directly from your code.</summary> void ISupportInitialize.EndInit() { if (this.IsUninitialized) { throw new InvalidOperationException(SR.Get("RelativeSourceNeedsMode")); } if (this._mode == RelativeSourceMode.FindAncestor && this.AncestorType == null) { throw new InvalidOperationException(SR.Get("RelativeSourceNeedsAncestorType")); } }
/// <summary>Indicates whether the <see cref="P:System.Windows.Data.RelativeSource.AncestorType" /> property should be persisted.</summary> /// <returns>true if the property value has changed from its default; otherwise, false.</returns> public bool ShouldSerializeAncestorType() { return this._mode == RelativeSourceMode.FindAncestor; }
/// <summary>Indicates whether the <see cref="P:System.Windows.Data.RelativeSource.AncestorLevel" /> property should be persisted.</summary> /// <returns>true if the property value has changed from its default; otherwise, false.</returns> public bool ShouldSerializeAncestorLevel() { return this._mode == RelativeSourceMode.FindAncestor; }
/// <summary>Returns an object that should be set as the value on the target object's property for this markup extension. For <see cref="T:System.Windows.Data.RelativeSource" />, this is another <see cref="T:System.Windows.Data.RelativeSource" />, using the appropriate source for the specified mode. </summary> /// <returns>Another <see cref="T:System.Windows.Data.RelativeSource" />.</returns> /// <param name="serviceProvider">An object that can provide services for the markup extension. In this implementation, this parameter can be null.</param> public override object ProvideValue(IServiceProvider serviceProvider) { if (this._mode == RelativeSourceMode.PreviousData) { return RelativeSource.PreviousData; } if (this._mode == RelativeSourceMode.Self) { return RelativeSource.Self; } if (this._mode == RelativeSourceMode.TemplatedParent) { return RelativeSource.TemplatedParent; } return this; } private void InitializeMode(RelativeSourceMode mode) { if (mode == RelativeSourceMode.FindAncestor) { this._ancestorLevel = 1; this._mode = mode; return; } if (mode == RelativeSourceMode.PreviousData || mode == RelativeSourceMode.Self || mode == RelativeSourceMode.TemplatedParent) { this._ancestorLevel = 0; this._mode = mode; return; } throw new ArgumentException(SR.Get("RelativeSourceModeInvalid"), "mode"); } } }