2008-09-17 20 views

Respuesta

667

Si desea enlazar a otra propiedad en el objeto:

{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}} 

Si desea obtener una propiedad en un antepasado:

{Binding Path=PathToProperty, 
    RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}} 

Si desea obtener una propiedad en la matriz de plantilla (para que pueda hacer 2 fijaciones camino en un ControlTemplate)

{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}} 

o, más cortas (esto sólo funciona para los enlaces de un solo sentido):

{TemplateBinding Path=PathToProperty} 
+4

Para éste "{ruta de enlace = PathToProperty, RelativeSource = {RelativeSource AncestorType = {x: Tipo typeOfAncestor}}}", parece que tiene que tener "Modo = FindAncestor," antes "AncestorType" – EdwardM

+0

Por lo que la tecnología ? En WPF, eso se infiere cuando especifica un 'AncestorType'. –

+1

Estoy de acuerdo con @EdwardM. Cuando omito 'FindAncestor', antes' AncestorType', aparece el siguiente error: "RelativeSource no está en el modo FindAncestor". (En VS2013, versión de comunidad) – kmote

17

No se olvide TemplatedParent:

<Binding RelativeSource="{RelativeSource TemplatedParent}"/> 

o

{Binding RelativeSource={RelativeSource TemplatedParent}} 
118
Binding RelativeSource={ 
    RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType} 
} 
... 

el atributo predeterminado de RelativeSource es propiedad Mode. Se da un conjunto completo de valores válidos aquí (from MSDN):

  • PreviousData le permite enlazar el elemento de datos anterior (no es que el control que contiene el elemento de datos) en la lista de elementos de datos que se muestra.

  • TemplatedParent Hace referencia al elemento al que se aplica la plantilla (en la que existe el elemento vinculado a datos). Esto es similar a establecer una TemplateBindingExtension y solo es aplicable si el enlace está dentro de una plantilla.

  • Auto Hace referencia al elemento sobre el que está configurando el enlace y le permite vincular una propiedad de ese elemento a otra propiedad del mismo elemento.

  • FindAncestor Hace referencia al antecesor de la cadena principal del elemento vinculado a datos. Puede usar esto para enlazar a un antecesor de un tipo específico o sus subclases. Este es el modo que usa si quiere especificar AncestorType y/o AncestorLevel.

12

Es digno de notar que para aquellos tropezarse con esta forma de pensar de Silverlight:

Silverlight ofrece un subconjunto reducido solamente, de estos comandos

+0

Sí, estaba buscando soporte SL también. Vívelo: https://connect.microsoft.com/VisualStudio/feedback/details/480603/findancestor-relativesource-should-be-available-in-silverlight – TravisWhidden

10

solo he publicado another solution para acceder a la DataContext de un padre elemento en Silverlight que funciona para mí. Usa Binding ElementName.

109

He aquí una explicación más visual en el contexto de una arquitectura MVVM:

enter image description here

+7

Lea mucho sobre RelativeSource pero nunca vio algo como SIMPLE y ¡CLARO como este pequeño gráfico! Gracias Gracias gracias. – SRO

+10

¿extrañé algo? ¿Cómo se puede considerar que un gráfico simple y claro? 1: los cuadros en el significado de la izquierda no están realmente relacionados con los de la derecha (¿por qué hay un archivo .cs dentro de ViewModel?) 2: ¿a qué apuntan estas flechas de DataContext? 3: ¿por qué la propiedad Message no está en ViewModel1? y lo más importante 5: ¿Por qué necesita una vinculación de RelativeSource para llegar al DataContext de la ventana si el bloque de texto ya tiene ese mismo DataContext? Claramente me falta algo aquí, así que o soy bastante tonto o este gráfico no es tan simple y claro como todos piensan. Por favor, ilumíname –

+2

@ MarkusHütter El diagrama muestra un grupo de Vistas anidadas y los Modelos de Vista correspondientes. El DataContext de View1 es ViewModel1, pero quiere vincularse a una propiedad de BaseViewModel. Como BaseViewModel es el DataContext de BaseView (que es una ventana), puede hacerlo encontrando el primer contenedor primario que es una ventana y tomando su DataContext. –

13

creé una biblioteca para simplificar la sintaxis de enlace de WPF incluyendo por lo que es más fácil de usar RelativeSource. Aquí hay unos ejemplos. Antes:

{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}} 
{Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}} 
{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}} 
{Binding Path=Text, ElementName=MyTextBox} 

Después:

{BindTo PathToProperty} 
{BindTo Ancestor.typeOfAncestor.PathToProperty} 
{BindTo Template.PathToProperty} 
{BindTo #MyTextBox.Text} 

Aquí está un ejemplo de cómo se simplifica enlace de método. Antes:

// C# code 
private ICommand _saveCommand; 
public ICommand SaveCommand { 
get { 
    if (_saveCommand == null) { 
    _saveCommand = new RelayCommand(x => this.SaveObject()); 
    } 
    return _saveCommand; 
} 
} 

private void SaveObject() { 
// do something 
} 

// XAML 
{Binding Path=SaveCommand} 

Después:

// C# code 
private void SaveObject() { 
// do something 
} 

// XAML 
{BindTo SaveObject()} 

puede encontrar la biblioteca aquí: http://www.simplygoodcode.com/2012/08/simpler-wpf-binding.html

Nota en el 'antes' ejemplo que utilizo para la unión que el código método ya fue optimizado mediante el uso de RelayCommand que la última vez que verifiqué no es una parte nativa de WPF. Sin eso, el ejemplo 'ANTES' hubiera sido aún más largo.

+1

Este tipo de ejercicios de mano demuestran el debilidad de XAML; * forma * muy complicado. – dudeNumber4

11

Dado que esta es la parte superior de Google para este problema, pensé que lo haría algunas partes y piezas útiles:
de cómo hacerlo en su mayoría en código aquí:

Binding b = new Binding(); 
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, this.GetType(), 1); 
b.Path = new PropertyPath("MyElementThatNeedsBinding"); 
MyLabel.SetBinding(ContentProperty, b); 

he copiado en gran medida esto desde: http://social.msdn.microsoft.com/Forums/en/wpf/thread/c5a59f07-c932-4715-8774-fa7e8472b75b

Además, la página de MSDN es bastante bueno en cuanto a ejemplos van: http://msdn.microsoft.com/en-us/library/system.windows.data.relativesource.aspx

+3

Mi vago recuerdo de WPF es que hacer enlaces en el código probablemente no sea lo mejor. –

40

Imagínese este caso, un rectángulo Que queremos que su altura sea siempre igual a su ancho, digamos un cuadrado. Podemos hacer esto utilizando el nombre del elemento

<Rectangle Fill="Red" Name="rectangle" 
        Height="100" Stroke="Black" 
        Canvas.Top="100" Canvas.Left="100" 
        Width="{Binding ElementName=rectangle, 
        Path=Height}"/> 

Pero en este caso por encima de que estamos obligados a indicar el nombre del objeto de enlace, es decir, el rectángulo. Podemos alcanzar el mismo propósito diferente utilizando la RelativeSource

<Rectangle Fill="Red" Height="100" 
        Stroke="Black" 
        Width="{Binding RelativeSource={RelativeSource Self}, 
        Path=Height}"/> 

Para ese caso, no estamos obligados a mencionar el nombre del objeto de vinculación y el ancho será siempre igual a la altura cada vez que se cambia la altura.

Si desea configurar el ancho para que sea la mitad de la altura, puede hacerlo agregando un convertidor a la extensión de marcado de encuadernación. Imaginemos ahora otro caso:

<TextBlock Width="{Binding RelativeSource={RelativeSource Self}, 
        Path=Parent.ActualWidth}"/> 

El caso anterior se usa para atar una determinada propiedad de un elemento dado a uno de sus queridos padres directos ya que este elemento tiene una propiedad que se llama Padre. Esto nos lleva a otro modo fuente relativo que es FindAncestor.

+4

Lo mejor es citar su fuente al copiar y pegar. – Jeremy

+1

@Jeremy se refiere a [este tutorial] (http://www.c-sharpcorner.com/UploadFile/yougerthen/relativesources-in-wpf/) por Bechir Bejaoui del 11 de mayo de 2010. –

+0

@ZachMierzejewski Gracias, SO la respuesta se lee como párrafos y faltan ejemplos. –

34

Bechir Bejaoui expone los casos de uso de los RelativeSources en WPF en his article here:

The RelativeSource is a markup extension that is used in particular binding cases when we try to bind a property of a given object to another property of the object itself, when we try to bind a property of a object to another one of its relative parents, when binding a dependency property value to a piece of XAML in case of custom control development and finally in case of using a differential of a series of a bound data. All of those situations are expressed as relative source modes. I will expose all of those cases one by one.

  1. Mode Self:

Imagine this case, a rectangle that we want that its height is always equal to its width, a square let's say. We can do this using the element name

<Rectangle Fill="Red" Name="rectangle" 
       Height="100" Stroke="Black" 
       Canvas.Top="100" Canvas.Left="100" 
       Width="{Binding ElementName=rectangle, 
       Path=Height}"/> 

But in this above case we are obliged to indicate the name of the binding object, namely the rectangle. We can reach the same purpose differently using the RelativeSource

<Rectangle Fill="Red" Height="100" 
       Stroke="Black" 
       Width="{Binding RelativeSource={RelativeSource Self}, 
       Path=Height}"/> 

For that case we are not obliged to mention the name of the binding object and the Width will be always equal to the Height whenever the height is changed.

If you want to parameter the Width to be the half of the height then you can do this by adding a converter to the Binding markup extension. Let's imagine another case now:

<TextBlock Width="{Binding RelativeSource={RelativeSource Self}, 
       Path=Parent.ActualWidth}"/> 

The above case is used to tie a given property of a given element to one of its direct parent ones as this element holds a property that is called Parent. This leads us to another relative source mode which is the FindAncestor one.

  1. Mode FindAncestor

In this case, a property of a given element will be tied to one of its parents, Of Corse. The main difference with the above case is the fact that, it's up to you to determine the ancestor type and the ancestor rank in the hierarchy to tie the property. By the way try to play with this piece of XAML

<Canvas Name="Parent0"> 
    <Border Name="Parent1" 
      Width="{Binding RelativeSource={RelativeSource Self}, 
      Path=Parent.ActualWidth}" 
      Height="{Binding RelativeSource={RelativeSource Self}, 
      Path=Parent.ActualHeight}"> 
     <Canvas Name="Parent2"> 
      <Border Name="Parent3" 
      Width="{Binding RelativeSource={RelativeSource Self}, 
      Path=Parent.ActualWidth}" 
      Height="{Binding RelativeSource={RelativeSource Self}, 
       Path=Parent.ActualHeight}"> 
       <Canvas Name="Parent4"> 
       <TextBlock FontSize="16" 
       Margin="5" Text="Display the name of the ancestor"/> 
       <TextBlock FontSize="16" 
       Margin="50" 
      Text="{Binding RelativeSource={RelativeSource 
         FindAncestor, 
         AncestorType={x:Type Border}, 
         AncestorLevel=2},Path=Name}" 
         Width="200"/> 
       </Canvas> 
      </Border> 
     </Canvas> 
    </Border> 
    </Canvas> 

The above situation is of two TextBlock elements those are embedded within a series of borders and canvas elements those represent their hierarchical parents. The second TextBlock will display the name of the given parent at the relative source level.

So try to change AncestorLevel=2 to AncestorLevel=1 and see what happens. Then try to change the type of the ancestor from AncestorType=Border to AncestorType=Canvas and see what's happens.

The displayed text will change according to the Ancestor type and level. Then what's happen if the ancestor level is not suitable to the ancestor type? This is a good question, I know that you're about to ask it. The response is no exceptions will be thrown and nothings will be displayed at the TextBlock level.

  1. TemplatedParent

This mode enables tie a given ControlTemplate property to a property of the control that the ControlTemplate is applied to. To well understand the issue here is an example bellow

<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> 
    <Canvas Name="Parent0"> 
    <Button Margin="50" 
       Template="{StaticResource template}" Height="0" 
       Canvas.Left="0" Canvas.Top="0" Width="0"> 
     <TextBlock FontSize="22">Click me</TextBlock> 
    </Button> 
</Canvas> 

If I want to apply the properties of a given control to its control template then I can use the TemplatedParent mode. There is also a similar one to this markup extension which is the TemplateBinding which is a kind of short hand of the first one, but the TemplateBinding is evaluated at compile time at the contrast of the TemplatedParent which is evaluated just after the first run time. As you can remark in the bellow figure, the background and the content are applied from within the button to the control template.

8

Este es un ejemplo de la utilización de este patrón que trabajó para mí en DataGrids vacías.

<Style.Triggers> 
    <DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}" Value="0"> 
     <Setter Property="Background"> 
      <Setter.Value> 
       <VisualBrush Stretch="None"> 
        <VisualBrush.Visual> 
         <TextBlock Text="We did't find any matching records for your search..." FontSize="16" FontWeight="SemiBold" Foreground="LightCoral"/> 
        </VisualBrush.Visual> 
       </VisualBrush> 
      </Setter.Value> 
     </Setter> 
    </DataTrigger> 
</Style.Triggers> 
13

En WPFRelativeSource expone de unión 3 properties para establecer:

1. Modo: Este es un enum que podría tener cuatro valores:

a. PreviousData(value=0) : It assigns the previous value of the property to the bound one

b. TemplatedParent(value=1) : This is used when defining the templates of any control and want to bind to a value/Property of the control .

Ex. define ControlTemplate

<ControlTemplate> 
     <CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> 
</ControlTemplate> 

c. Self(value=2) : When want to bind from a self or a property of self.

Ex. Send checked state of checkbox as CommandParameter while setting the Command on CheckBox

<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" /> 

d. FindAncestor(value=3) : When want to bind from a parent control in Visual Tree .

Ex. bind a checkbox in records if a grid ,if header checkbox is checked

<CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type iDP:XamDataGrid}},Path=DataContext.IsHeaderChecked,Mode=TwoWay}" /> 

2. AncestorType: cuando el modo es FindAncestor entonces definir qué tipo de ancestro

RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type iDP:XamDataGrid}} 

3. AncestorLevel: cuando el modo es FindAncestor entonces lo nivel de un elemento (si hay dos mismos tipos de padres i n visual tree)

RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type iDP:XamDataGrid,AncestorLevel=1}} 

Above are all use-cases for RelativeSource binding.

Here is a ref link

8

no he leído todas las respuestas pero sólo quiero añadir esta información en caso de Comando fuente de unión relativa de un botón. Cuando se utiliza la fuente en relación con Mode=FindAncestor, la unión debe ser como:

Command="{Binding Path=DataContext.CommandProperty, RelativeSource={...}}" 

Si no se agrega DataContext en su camino, en tiempo de ejecución no puede recuperar la propiedad.

4

Si un elemento no es parte del árbol visual, entonces RelativeSource nunca funcionará.

En este caso, debe probar una técnica diferente, iniciada por Thomas Levesque.

Él tiene la solución en su blog bajo [WPF] How to bind to data when the DataContext is not inherited. ¡Y funciona absolutamente genial!

En el caso improbable de que su blog esté inactivo, el Apéndice A contiene una copia duplicada de his article.

Por favor, no comentamos aquí, por favor comment directly on his blog post.

Apéndice A: Espejo de la entrada del blog

La propiedad DataContext en WPF es muy práctico, ya que se hereda automáticamente por todos los hijos del elemento donde se asigna; por lo tanto, no es necesario volver a establecerlo en cada elemento que desee vincular. Sin embargo, en algunos casos, el DataContext no es accesible: ocurre para elementos que no son parte del árbol visual o lógico. Puede ser muy difícil enlazar una propiedad en esos elementos ...

Vamos a ilustrarlo con un ejemplo simple: queremos mostrar una lista de productos en un DataGrid. En la cuadrícula, queremos poder mostrar u ocultar la columna Precio, en función del valor de una propiedad ShowPrice expuesta por ViewModel. El enfoque obvio es obligar a la visibilidad de la columna a la propiedad ShowPrice:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False" 
       Visibility="{Binding ShowPrice, 
       Converter={StaticResource visibilityConverter}}"/> 

Desafortunadamente, cambiando el valor de ShowPrice no tiene ningún efecto, y la columna es siempre visible ... ¿por qué? Si nos fijamos en la ventana Resultados de Visual Studio, nos damos cuenta de la siguiente línea:

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=ShowPrice; DataItem=null; target element is ‘DataGridTextColumn’ (HashCode=32685253); target property is ‘Visibility’ (type ‘Visibility’)

The message is rather cryptic, but the meaning is actually quite simple: WPF doesn’t know which FrameworkElement to use to get the DataContext, because the column doesn’t belong to the visual or logical tree of the DataGrid.

podemos tratar de ajustar la unión para obtener el resultado deseado, por ejemplo mediante el establecimiento de la RelativeSource a la misma cuadrícula de datos:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False" 
       Visibility="{Binding DataContext.ShowPrice, 
       Converter={StaticResource visibilityConverter}, 
       RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/> 

O podemos añadir un CheckBox con destino a ShowPrice, y tratar de obligar a la visibilidad de la columna a la propiedad IsChecked especificando el nombre del elemento:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False" 
       Visibility="{Binding IsChecked, 
       Converter={StaticResource visibilityConverter}, 
       ElementName=chkShowPrice}"/> 

Pero ninguno o Si estas soluciones parecen funcionar, siempre obtenemos el mismo resultado ...

En este punto, parece que el único enfoque viable sería cambiar la visibilidad de la columna en código subyacente, que generalmente preferimos evitar al usar Patrón MVVM ... Pero no voy a rendirme tan pronto, al menos no mientras haya otras opciones a considerar

La solución a nuestro problema es bastante simple, y aprovecha la clase Freezable. El objetivo principal de esta clase es definir objetos que tengan un estado modificable y de solo lectura, pero la característica interesante en nuestro caso es que los objetos Freezable pueden heredar el DataContext incluso cuando no están en el árbol visual o lógico. No sé el mecanismo exacto que permite este comportamiento, pero vamos a aprovecharlo para que nuestro trabajo vinculante ...

La idea es crear una clase (lo llamé BindingProxy por razones que deberían volverse obvio muy pronto) que hereda Se puede congelar y declara una propiedad de dependencia de datos:

public class BindingProxy : Freezable 
{ 
    #region Overrides of Freezable 

    protected override Freezable CreateInstanceCore() 
    { 
     return new BindingProxy(); 
    } 

    #endregion 

    public object Data 
    { 
     get { return (object)GetValue(DataProperty); } 
     set { SetValue(DataProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty DataProperty = 
     DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); 
} 

entonces podemos declarar una instancia de esta clase en los recursos de la cuadrícula de datos, y obligar a la propiedad de datos a la DataContext actual:

<DataGrid.Resources> 
    <local:BindingProxy x:Key="proxy" Data="{Binding}" /> 
</DataGrid.Resources> 

Último paso es especificar este objeto BindingProxy (fácilmente accesible con StaticResource) como la fuente de la unión:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False" 
       Visibility="{Binding Data.ShowPrice, 
       Converter={StaticResource visibilityConverter}, 
       Source={StaticResource proxy}}"/> 

Tenga en cuenta que la ruta de enlace se ha prefijado con “datos”, ya que el camino está ahora en relación con el objeto BindingProxy .

El enlace ahora funciona correctamente y la columna se muestra u oculta correctamente según la propiedad ShowPrice.

Cuestiones relacionadas