Vinculación de objetos definidos en código subyacente

88

Tengo algún objeto que se instancia en el código detrás, por ejemplo, el XAML se llama window.xaml y dentro de window.xaml.cs

protected Dictionary<string, myClass> myDictionary;

¿Cómo puedo vincular este objeto a, por ejemplo, una vista de lista, usando solo marcas XAML?

Actualizar:

(Esto es exactamente lo que tengo en mi código de prueba):

<Window x:Class="QuizBee.Host.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="{Binding windowname}" Height="300" Width="300"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
    </Grid>
</Window>

Y en código subyacente

public partial class Window1 : Window
{
    public const string windowname = "ABCDEFG";

    public Window1()
    {
        InitializeComponent();
    }
}

Supongamos que el título debería convertirse en "ABCDEFG", ¿verdad? pero termina sin mostrar nada.

X y Y
fuente
1
Por extraño que parezca, si cambio el orden de asignación de propiedad de la ventana, no funciona. Si configuro la propiedad "Título" seguida de la propiedad "DataContext", el enlace no ocurre. ¿Alguien puede explicar esto? <Ventana x: Class = "INotifyPropertyTest.MainWindow" xmlns = " schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns: x = " schemas.microsoft.com/winfx/2006/xaml " xmlns: local = " clr-namespace: INotifyPropertyTest "Height =" 350 "Width =" 525 "DataContext =" {Binding RelativeSource = {RelativeSource self}} "Title =" {Binding WindowName} ">
Ramesh

Respuestas:

109

Puede configurar el DataContext para su control, formulario, etc. así:

DataContext="{Binding RelativeSource={RelativeSource Self}}"

Aclaración :

El contexto de datos que se establece en el valor anterior debe realizarse en cualquier elemento que "posea" el código subyacente; por lo tanto, para una ventana, debe establecerlo en la declaración de la ventana.

Tengo tu ejemplo trabajando con este código:

<Window x:Class="MyClass"
  Title="{Binding windowname}"
  DataContext="{Binding RelativeSource={RelativeSource Self}}"
  Height="470" Width="626">

El DataContext establecido en este nivel es heredado por cualquier elemento en la ventana (a menos que lo cambie explícitamente por un elemento secundario), por lo que después de configurar el DataContext para la ventana, debería poder hacer un enlace directo a las propiedades CodeBehind desde cualquier control en la ventana.

Guy Starbuck
fuente
1
El "yo" aquí significa el control, en lugar de toda la clase de ventana, ¿verdad?
xandy
Es bastante extraño, a continuación se muestra el código que tengo y no funciona como se esperaba: clase pública parcial Window1: Window {public const string windowname = "ABCDEFG"; Public Window1 () {InitializeComponent (); }} <Ventana x: Class = "QuizBee.Host.Window1" xmlns = " schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns: x = " schemas.microsoft.com/winfx/2006/xaml " Título = "{Binding windowname}" Height = "300" Width = "300" DataContext = "{Binding RelativeSource = {RelativeSource Self}}"> </Window>
xandy
9
Oh, está bien ahora, cambié el nombre de la ventana para que sea una propiedad en lugar de una variable pública pura y se puede mostrar ahora. ¡Gracias!
xandy
1
No puedo imaginar por qué esto no está configurado de forma predeterminada.
Okonomiyaki3000
122

Hay una forma mucho más sencilla de hacer esto. Puede asignar un nombre a su ventana o UserControl, y luego enlazar por ElementName.

Window1.xaml

<Window x:Class="QuizBee.Host.Window1"
        x:Name="Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ListView ItemsSource="{Binding ElementName=Window1, Path=myDictionary}" />
</Window>

Window1.xaml.cs

public partial class Window1:Window
{
    // the property must be public, and it must have a getter & setter
    public Dictionary<string, myClass> myDictionary { get; set; }

    public Window1()
    {
        // define the dictionary items in the constructor
        // do the defining BEFORE the InitializeComponent();

        myDictionary = new Dictionary<string, myClass>()
        {
            {"item 1", new myClass(1)},
            {"item 2", new myClass(2)},
            {"item 3", new myClass(3)},
            {"item 4", new myClass(4)},
            {"item 5", new myClass(5)},
        }; 

        InitializeComponent();
    }
}
Saad Imran.
fuente
3
Tuve que cambiar x: Name (error del compilador CS0542). Entonces ElementName debe cambiarse en consecuencia.
Jack Miller
25

Si bien la respuesta de Guy es correcta (y probablemente se ajuste a 9 de cada 10 casos), vale la pena señalar que si está intentando hacer esto desde un control que ya tiene su DataContext configurado más arriba en la pila, lo restablecerá cuando configure DataContext de vuelta a sí mismo:

DataContext="{Binding RelativeSource={RelativeSource Self}}"

Esto, por supuesto, romperá sus enlaces existentes.

Si este es el caso, debe establecer RelativeSource en el control que está intentando vincular, en lugar de en su padre.

es decir, para vincularse a las propiedades de UserControl:

Binding Path=PropertyName, 
        RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}

Dado lo difícil que puede ser actualmente ver qué está pasando con el enlace de datos, vale la pena tener esto en cuenta incluso si encuentra que la configuración RelativeSource={RelativeSource Self}funciona actualmente :)

CatBusStop
fuente
1
Silverlight 4 no es compatible con FindAncestor. Sin embargo, si necesita hacerlo de esta manera, puede implementar FindAncestor como se describe en este sitio. http://blog.thekieners.com/2010/09/08/relativesource-binding-with-findancestor-mode-in-silverlight/
ShawnFeatherly
7

Solo un poco más de aclaración: una propiedad sin 'get', 'set' no podrá vincularse

Estoy enfrentando el caso como el caso del autor de la pregunta. Y debo tener las siguientes cosas para que el enlace funcione correctamente:

//(1) Declare a property with 'get','set' in code behind
public partial class my_class:Window {
  public String My_Property { get; set; }
  ...

//(2) Initialise the property in constructor of code behind
public partial class my_class:Window {
  ...
  public my_class() {
     My_Property = "my-string-value";
     InitializeComponent();
  }

//(3) Set data context in window xaml and specify a binding
<Window ...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
  <TextBlock Text="{Binding My_Property}"/>
</Window>
Jondinham
fuente
9
¿Cómo exactamente se puede tener una propiedad sin 'get' y 'set'? ¿No sería eso un campo y no una propiedad?
kjbartel
1

Defina un convertidor:

public class RowIndexConverter : IValueConverter
{
    public object Convert( object value, Type targetType,
                           object parameter, CultureInfo culture )
    {
        var row = (IDictionary<string, object>) value;
        var key = (string) parameter;
        return row.Keys.Contains( key ) ? row[ key ] : null;
    }

    public object ConvertBack( object value, Type targetType,
                               object parameter, CultureInfo culture )
    {
        throw new NotImplementedException( );
    }
}

Enlazar a una definición personalizada de un diccionario. Hay muchas anulaciones que he omitido, pero el indexador es el importante, porque emite el evento de cambio de propiedad cuando se cambia el valor. Esto es necesario para el enlace de origen a destino.

public class BindableRow : INotifyPropertyChanged, IDictionary<string, object>
{
    private Dictionary<string, object> _data = new Dictionary<string, object>( );

    public object Dummy   // Provides a dummy property for the column to bind to
    {
        get
        {
            return this;
        }
        set
        {
            var o = value;
        }
    }


    public object this[ string index ]
    {
        get
        {
            return _data[ index ];
        }
        set
        {
            _data[ index ] = value;
            InvokePropertyChanged( new PropertyChangedEventArgs( "Dummy" ) ); // Trigger update
        }
    }


}

En su archivo .xaml use este convertidor. Primera referencia a él:

<UserControl.Resources>
    <ViewModelHelpers:RowIndexConverter x:Key="RowIndexConverter"/>
</UserControl.Resources>

Entonces, por ejemplo, si su diccionario tiene una entrada donde la clave es "Nombre", entonces para enlazarla: use

<TextBlock  Text="{Binding Dummy, Converter={StaticResource RowIndexConverter}, ConverterParameter=Name}">
Phillip Ngan
fuente
1

Haga de su propiedad "nombre de ventana" una propiedad de dependencia y mantenga el resto igual.

viky
fuente
0

En su código subyacente, configure el DataContext de la ventana en el diccionario. En su XAML, puede escribir:

<ListView ItemsSource="{Binding}" />

Esto vinculará ListView al diccionario.

Para escenarios más complejos, este sería un subconjunto de técnicas detrás del patrón MVVM .

Szymon Rozga
fuente
0

Una forma sería crear un ObservableCollection (System.Collections.ObjectModel) y tener los datos del diccionario allí. Entonces debería poder vincular ObservableCollection a su ListBox.

En su XAML debería tener algo como esto:

<ListBox ItemsSource="{Binding Path=Name_of_your_ObservableCollection" />
Parcial
fuente
0

Estaba teniendo exactamente el mismo problema, pero el mío no era porque estaba configurando una variable local ... Estaba en una ventana secundaria y necesitaba establecer un DataContext relativo que acabo de agregar a la ventana XAML.

<Window x:Class="Log4Net_Viewer.LogItemWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Title="LogItemWindow" Height="397" Width="572">
davesbrain
fuente
0

Puedes probar x: truco de referencia

<Window ... x:Name="myWindow"><ListBox ItemsSource="{Binding Items, Source={x:Reference myWindow}}" /></Window>
Николай Солдаткин
fuente