2014-01-23 4 views
0

Итак, я создаю программу, которая вытаскивает имена таблиц и полей из базы данных Microsoft Access и помещает их в два разных элемента списка. В качестве теста для моей привязки я установил одинаковые настройки обоих ящиков (просто простая копия/вставка, потому что я обогнал себя), и я получил имена таблиц для успешного связывания, но только с использованием lbTables.DataContext = this; (я пытался использовать lbTables.SetBinding (ListBox.DataContextProperty, new Binding ("MDBtoCSV.MainWindow"));, но это не так, по какой-то причине). DataContext, похоже, не наследует от окна над ним, которое из того, что я прочитал, - это то, что он должен делать.Предпочтительный/правильный способ привязки к MainWindow

Когда я начал пытаться привязать DataContext к lbFields в XAML, я нашел странную проблему. если я определяю и инициализирую коллекцию в одно и то же время, глобально (как показано ниже), private ObservableCollection<CheckedListItem<Table>> _listTables = new ObservableCollection<CheckedListItem<Label>>(); оба ListBox ведут себя так же, как и от них.

Но если я вместо этого инициализирую коллекцию в своем коде и использую lbTables.DataContext = this;, тогда только lbTables заполняется, пока lbFields остается пустым. Есть ли предпочтительный метод или менее хрупкий метод для явного определения DataContext и ItemSource?

Ниже моя XAML:

<ListBox x:Name="lbFields" HorizontalAlignment="Left" Height="70" Margin="10,113,0,0" VerticalAlignment="Top" Width="240" 
      DataContext="{Binding ElementName=appMainWindow, Mode=OneWay}" ItemsSource="{Binding Path=ListTables, Mode=OneWay}"> 
    </ListBox> 
</Grid> 

TL; DR: Почему бы DataContext не наследуется от окна? Почему не ListBox.DataContext = this действуют так же, как версия XAML?

Edit:

private ObservableCollection<CheckedListItem<Table>> _listTables;// = new  ObservableCollection<CheckedListItem<Table>>(); 

    public ObservableCollection<CheckedListItem<Table>> ListTables 
    { 
     get { return _listTables; } 
     private set { _listTables = value;} 
    } 

public class CheckedListItem<T> : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    private bool isChecked; 
    private T item; 
    private string name; 

    public string Name { get; set; } 

    public CheckedListItem() { } 

    public CheckedListItem (T item, bool isChecked = false) 
    { this.item = item; 
     this.isChecked = isChecked;} 

    public T Item 
    { get { return item; } 
     set 
     { item = value; 
      NotifyPropertyChanged("Item");} 
    } 

    public bool IsChecked 
    { get { return isChecked; } 
     set 
     { isChecked = value; 
      NotifyPropertyChanged ("IsChecked");} 
    } 

    public class Table 
    { public string name { get; set; } 
     private ObservableCollection<string> _listFields = new ObservableCollection<string> (
        new string[]{"null","null","null","null"}); 

     public ObservableCollection<string> ListFields { get { return _listFields; } set { _listFields = value; } } 
    } 

ответ

0

Все, что я делаю, оказалось очень хорошим, что я отсутствовал, это был INotifyPropertyChanged в MainWindow. Раньше я не мог понять, но тогда я нашел this, и это то, что я искал в течение трех месяцев.

0

Я настоятельно рекомендую вам взглянуть на MVVM pattern example и использовать это для WPF и привязки. Это делает все это лот проще.

Я бы предложил три класса ViewModel: MainWindowViewModel, DatabaseTableViewModel и DatabaseTableColumnViewModel. Как вы можете себе представить, что они вложены в каждом соответствующем одном с некоторыми свойствами их собственных, как так:

class MainWindowViewModel : ViewModelBase 
{ 
    // Make sure you implement INotifyPropertyChanged and invoke on each property! 

    public string Title { get; set; } 

    public ObservableCollection<DatabaseTableViewModel> Tables { get; set; } 

    public DatabaseTableViewModel SelectedTable { get; set; } 
} 

class DatabaseTableViewModel : ViewModelBase 
{ 
    public string TableName { get; set; } 

    public ObservableCollection<DatabaseTableColumnViewModel> Columns { get; set; } 
} 

class DatabaseTableColumnTableViewModel : ViewModelBase 
{ 
    public string ColumnName { get; set; } 
    public string ColumnType { get; set; } 

    public DatabaseTableColumnViewModel SelectedColumn { get; set; } 
} 

Так что ваши XAML привязки будет выглядеть примерно так:

<ListBox ItemsSource="{Binding Path=Tables}" 
     SelectedItem="{Binding Path=SelectedTable}"> 
</ListBox> 

<ListBox ItemsSource="{Binding Path=SelectedTable.Columns}" 
     SelectedItem="{Binding Path=SelectedTable.SelectedColumn}"> 
</ListBox> 

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

Вы должны указать MainWindow.DataContext экземпляру MainWindowViewModel.

+0

Я в основном придерживаюсь MVVM, и все мои свойства реализуют INotifyPropertyChanged, за исключением моего самого верхнего свойства, которое содержит список имен таблиц. Он находится в главном окне, и я не уверен, как реализовать INotifyPropertyChanged, кроме наследования его в классе. Кроме того, вы по-прежнему неявно предполагаете DataContext, который, прежде всего, я считаю своей проблемой. –

+0

Пока ваши объекты 'ViewModel' реализуют' INotifyPropertyChanged', а ваш набор 'set {...}' вашего свойства правильно запускает обработчик событий, все будет в порядке. 'ObservableCollection ' обрабатывает его также. Ваш «DataContext» подразумевается с помощью «Window.DataContext», поэтому создается «MainWindowViewModel», который должен содержать все модели представлений, которые необходимо отобразить. – Erik

+0

За исключением DataContext неявно назначается. Если я удалю 'lbTables.DataContext = this;' из моего кода, я потеряю привязку для lbTables, пока поддерживает lbFields, потому что его DataContext жестко закодирован в XAML. –

Смежные вопросы