TransWikia.com

Binding objects defined in code-behind

Stack Overflow Asked by xandy on December 27, 2020

I have some object that is instantiated in code behind, for instance, the XAML is called window.xaml and within the window.xaml.cs

protected Dictionary<string, myClass> myDictionary;

How can I bind this object to, for example, a list view, using only XAML markups?

Update:

(This is exactly I have in my test code):

<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>

And in codebehind

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

    public Window1()
    {
        InitializeComponent();
    }
}

Suppose the title should become “ABCDEFG” right? but it ends up showing nothing.

11 Answers

You can set the DataContext for your control, form, etc. like so:

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

Clarification:

The data context being set to the value above should be done at whatever element "owns" the code behind -- so for a Window, you should set it in the Window declaration.

I have your example working with this code:

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

The DataContext set at this level then is inherited by any element in the window (unless you explicitly change it for a child element), so after setting the DataContext for the Window you should be able to just do straight binding to CodeBehind properties from any control on the window.

Correct answer by Guy Starbuck on December 27, 2020

That's my way to bind to code behind (see property DataTemplateSelector)

public partial class MainWindow : Window
{
  public MainWindow()
  {
    this.DataTemplateSelector = new MyDataTemplateSelector();

    InitializeComponent();

    // ... more initializations ...
  }

  public DataTemplateSelector DataTemplateSelector { get; }

  // ... more code stuff ...
}

In XAML will referenced by RelativeSource via Ancestors up to containing Window, so I'm at my Window class and use the property via Path declaration:

<GridViewColumn Header="Value(s)"
                CellTemplateSelector="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataTemplateSelector}"/>

Setting of property DataTemplateSelector before call InitializeComponent depends on missing implementation of IPropertyChanged or use of implementation with DependencyProperty so no communication run on change of property DataTemplateSelector.

Answered by VBWebProfi on December 27, 2020

You can try x:Reference trick

<Window ... x:Name="myWindow"><ListBox ItemsSource="{Binding Items, Source={x:Reference myWindow}}" /></Window>

Answered by Николай Солдаткин on December 27, 2020

Just a little more clarification: A property without 'get','set' won't be able to be bound

I'm facing the case just like the asker's case. And I must have the following things in order for the bind to work properly:

//(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>

Answered by jondinham on December 27, 2020

I was having this exact same problem but mine wasn't because I was setting a local variable... I was in a child window, and I needed to set a relative DataContext which I just added to the Window 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">

Answered by davesbrain on December 27, 2020

There's a much easier way of doing this. You can assign a Name to your Window or UserControl, and then binding by 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();
    }
}

Answered by Saad Imran. on December 27, 2020

While Guy's answer is correct (and probably fits 9 out of 10 cases), it's worth noting that if you are attempting to do this from a control that already has its DataContext set further up the stack, you'll resetting this when you set DataContext back to itself:

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

This will of course then break your existing bindings.

If this is the case, you should set the RelativeSource on the control you are trying to bind, rather than its parent.

i.e. for binding to a UserControl's properties:

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

Given how difficult it can be currently to see what's going on with data binding, it's worth bearing this in mind even if you find that setting RelativeSource={RelativeSource Self} currently works :)

Answered by CatBusStop on December 27, 2020

Make your property "windowname" a DependencyProperty and keep the remaining same.

Answered by viky on December 27, 2020

Define a converter:

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( );
    }
}

Bind to a custom definition of a Dictionary. There's lot of overrides that I've omitted, but the indexer is the important one, because it emits the property changed event when the value is changed. This is required for source to target binding.

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
        }
    }


}

In your .xaml file use this converter. First reference it:

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

Then, for instance, if your dictionary has an entry where the key is "Name", then to bind to it: use

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

Answered by Phillip Ngan on December 27, 2020

One way would be to create an ObservableCollection (System.Collections.ObjectModel) and have your dictionary data in there. Then you should be able to bind the ObservableCollection to your ListBox.

In your XAML you should have something like this:

<ListBox ItemsSource="{Binding Path=Name_of_your_ObservableCollection" />

Answered by Partial on December 27, 2020

In your code behind, set the window's DataContext to the dictionary. In your XAML, you can write:

<ListView ItemsSource="{Binding}" />

This will bind the ListView to the dictionary.

For more complex scenarios, this would be a subset of techniques behind the MVVM pattern.

Answered by Szymon Rozga on December 27, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP