Привязка объектов, определенных в коде программной части


88

У меня есть объект, который создается в коде позади, например, XAML называется window.xaml и внутри window.xaml.cs

protected Dictionary<string, myClass> myDictionary;

Как я могу привязать этот объект, например, к представлению списка, используя только разметку XAML?

Обновить:

(Это как раз и есть в моем тестовом коде):

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

И в коде

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

    public Window1()
    {
        InitializeComponent();
    }
}

Предположим, заголовок должен стать «ABCDEFG», верно? но в итоге ничего не показывает.


1
Как ни странно, если я изменю порядок назначения свойств окна, это не сработает. Если я устанавливаю свойство «Заголовок», за которым следует свойство «DataContext», привязка не происходит. Кто-нибудь может это объяснить? <Window 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} ">
Рамеш

Ответы:


109

Вы можете установить DataContext для своего элемента управления, формы и т. Д. Следующим образом:

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

Уточнение :

Контекст данных, устанавливаемый на значение выше, должен выполняться в любом элементе, «владеющем» кодом позади - поэтому для Window вы должны установить его в объявлении Window.

У меня есть ваш пример, работающий с этим кодом:

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

Затем DataContext, установленный на этом уровне, наследуется любым элементом в окне (если вы явно не измените его для дочернего элемента), поэтому после установки DataContext для Window вы сможете просто выполнить прямую привязку к свойствам CodeBehind из любого элемента управления на окне.


1
«Я» здесь означает элемент управления, а не весь класс окна, не так ли?
xandy

Как ни странно, вот код, который у меня есть, и он работает не так, как ожидалось: публичный частичный класс Window1: Window {public const string windowname = "ABCDEFG"; общедоступное Window1 () {InitializeComponent (); }} <Window x: Class = "QuizBee.Host.Window1" xmlns = " schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns: x = " schemas.microsoft.com/winfx/2006/xaml " Название = "{Binding windowname}" Height = "300" Width = "300" DataContext = "{Binding RelativeSource = {RelativeSource Self}}"> </Window>
xandy

10
О, теперь все в порядке, я изменил имя окна на свойство вместо чистой общедоступной переменной, и теперь оно может отображаться! Благодарность!
xandy

1
Я не могу себе представить, почему это не просто установлено по умолчанию.
Okonomiyaki3000

122

Есть более простой способ сделать это. Вы можете назначить имя своему окну или пользовательскому элементу управления, а затем привязать его к элементу 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();
    }
}

3
Пришлось изменить x: Name (ошибка компилятора CS0542). Затем необходимо соответствующим образом изменить ElementName.
Джек Миллер,

25

Хотя ответ Гая правильный (и, вероятно, подходит для 9 из 10 случаев), стоит отметить, что если вы пытаетесь сделать это из элемента управления, у которого уже есть DataContext, установленный дальше в стеке, вы сбросите его, когда установите DataContext обратно к себе:

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

Это, конечно же, нарушит ваши существующие привязки.

В этом случае вы должны установить RelativeSource для элемента управления, который вы пытаетесь привязать, а не для его родительского элемента.

т.е. для привязки к свойствам UserControl:

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

Учитывая, насколько сложно в настоящее время увидеть, что происходит с привязкой данных, стоит иметь это в виду, даже если вы обнаружите, что этот параметр в RelativeSource={RelativeSource Self}настоящее время работает :)


1
Silverlight 4 не поддерживает FindAncestor. Однако если вам нужно сделать это таким образом, вы можете реализовать FindAncestor, как описано на этом сайте. http://blog.thekieners.com/2010/09/08/relativesource-binding-with-findancestor-mode-in-silverlight/
ShawnFeatherly,

6

Еще немного пояснений: свойство без get, set не может быть привязано

Я сталкиваюсь с делом так же, как со случаем спрашивающего. И для правильной работы привязки у меня должны быть следующие вещи:

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

9
Как именно у вас может быть собственность без «получить» и «установить»? Разве это не поле, а не собственность?
kjbartel

1

Определите конвертер:

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

Привязка к пользовательскому определению словаря. Я пропустил много переопределений, но индексатор является важным, потому что он генерирует событие изменения свойства при изменении значения. Это необходимо для привязки источника к цели.

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


}

В вашем файле .xaml используйте этот конвертер. Сначала укажите это:

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

Затем, например, если в вашем словаре есть запись с ключом «Имя», то для привязки к ней: используйте

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

1

Сделайте свое свойство "windowname" DependencyProperty, а остальные оставьте без изменений.


0

В вашем коде установите DataContext окна в словарь. В вашем XAML вы можете написать:

<ListView ItemsSource="{Binding}" />

Это свяжет ListView со словарем.

Для более сложных сценариев это будет подмножество методов, лежащих в основе шаблона MVVM .


0

Один из способов - создать ObservableCollection (System.Collections.ObjectModel) и разместить там данные словаря. Затем вы сможете привязать ObservableCollection к вашему ListBox.

В вашем XAML должно быть что-то вроде этого:

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

0

У меня была такая же проблема, но у меня не было, потому что я устанавливал локальную переменную ... Я находился в дочернем окне, и мне нужно было установить относительный DataContext, который я только что добавил в 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">


0

Это мой способ привязки к коду (см. Свойство DataTemplateSelector)

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

    InitializeComponent();

    // ... more initializations ...
  }

  public DataTemplateSelector DataTemplateSelector { get; }

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

В XAML будет ссылаться RelativeSourceчерез Ancestors до содержания Window, поэтому я нахожусь в своем Windowклассе и использую свойство через Pathобъявление:

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

Установка свойства DataTemplateSelectorперед вызовом InitializeComponentзависит от отсутствующей реализации IPropertyChangedили использования реализации, DependencyPropertyпоэтому обмен данными при изменении свойства не происходит DataTemplateSelector.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.