Установка DataContext пользовательского элемента управления из-за кода - PullRequest
3 голосов
/ 09 июля 2009

Это должно быть довольно легко, но это бросает VS2008 для серьезного цикла.

Я пробую WPF с MVVM, и я новичок в этом, хотя я занимаюсь разработкой около 15 лет и у меня есть комп. Sci. степень. На текущем клиенте я обязан использовать VB.Net.

Я переименовал свои собственные переменные и удалил некоторые отвлекающие факторы в коде ниже, поэтому, пожалуйста, прости меня, если он не на 100% синтаксически совершенен! Вам, вероятно, не нужен код, чтобы понять вопрос, но я включаю его на случай, если он поможет.

У меня очень простой файл MainView.xaml:

<Window x:Class="MyApp.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main Window" Height="400" Width="800" Name="MainWindow">

<Button Name="Button1">Show Grid</Button>
<StackPanel Name="teststack" Visibility="Hidden"/>

</Window>

У меня также есть UserControl, называемый DataView, который состоит из DataGrid:

<UserControl x:Class="MyApp.Views.DataView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfToolkit="http://schemas.microsoft.com/wpf/2008/toolkit" >
<Grid>
    <WpfToolkit:DataGrid 
        ItemsSource="{Binding Path=Entries}" SelectionMode="Extended">
    </WpfToolkit:DataGrid>
</Grid>
</UserControl>

Конструктор для пользовательского управления DataView устанавливает DataContext, привязывая его к модели представления, как показано здесь:

Partial Public Class DataView

    Dim dataViewModel As ViewModels.DataViewModel

    Public Sub New()
        InitializeComponent()

        dataViewModel = New ViewModels.DataViewModel
        dataViewModel.LoadDataEntries()
        DataContext = dataViewModel
    End Sub

End Class

Модель представления для DataView выглядит следующим образом (в ViewModelBase не так много):

Public Class DataViewModel
    Inherits ViewModelBase

Public Sub New()
End Sub

Private _entries As ObservableCollection(Of DataEntryViewModel) = New ObservableCollection(Of DataEntryViewModel)
Public ReadOnly Property Entries() As ObservableCollection(Of DataEntryViewModel)
    Get
        Return _entries
    End Get
End Property

Public Sub LoadDataEntries()

    Dim dataEntryList As List(Of DataEntry) = DataEntry.LoadDataEntries()
    For Each dataentry As Models.DataEntry In dataEntryList
        _entries.Add(New DataEntryViewModel(dataentry))
    Next

    End Sub
End Class

Теперь этот UserControl работает просто отлично, если я его создаю в XAML. Когда я запускаю код, сетка обнаруживается и заполняет его просто отлично.

Однако для загрузки данных в сетку требуется много времени, и я хочу создать этот пользовательский элемент управления программно после нажатия кнопки, а не декларативно создавать экземпляр сетки в XAML. Я хочу создать экземпляр пользовательского элемента управления и вставить его как дочерний элемент элемента управления StackPanel:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click

    Dim dataView As New DataView
    teststack.Children.Add(dataView)

End Sub

Когда я делаю это, как только заканчивается Button1_Click, мое приложение блокируется, начинает использовать оперативную память и загружает процессор примерно на 50%.

Правильно ли я создаю экземпляр своего UserControl? Кажется, все сводится к назначению DataContext в конструкторе DataEntry. Если я это прокомментирую, приложение работает как положено (конечно, без сетки).

Если я переместу этот блок кода в Button1_Click (в основном переместив код конструктора DataEntry на уровень выше), приложение все равно завершится ошибкой:

dataViewModel = New ViewModels.DataViewModel
dataViewModel.LoadDataEntries()
dataView.DataContext = dataViewModel

Я в тупике. Кто-нибудь может дать мне несколько советов о том, что я могу делать неправильно, или даже как отлаживать бесконечный цикл, в который попадает мое приложение?

Большое спасибо.

Ответы [ 3 ]

1 голос
/ 09 июля 2009

Основной причиной вашей проблемы является либо необработанный объем загружаемых данных, либо некоторая неэффективность загрузки этих данных. Сказав это, причина, по которой вы видите блокировку приложения, заключается в том, что вы блокируете поток пользовательского интерфейса при загрузке данных.

Я полагаю, что в вашем первом случае загрузка данных была загружена в другой поток для загрузки данных. Во втором примере вы создаете экземпляр элемента управления в потоке пользовательского интерфейса, и в результате вся логика конструктора и загрузки выполняется в текущем потоке (потоке пользовательского интерфейса). Если вы перенесете эту работу в другой поток, вы должны увидеть результаты, аналогичные первому примеру.

1 голос
/ 10 июля 2009

В конце концов я перестал пытаться получить DataContext в наборе UserControl во время создания экземпляра UserControl (либо в XAML, либо в коде). Теперь я загружаю данные и устанавливаю DataContext из UserControl в событии в UserControl (IsVisibleChanged, я считаю). Когда я создаю экземпляр UserControl в XAML, его видимость устанавливается на Hidden. Когда кнопка 1 нажата, я устанавливаю видимость UserControl на Visible. Таким образом, UserControl появляется, и он загружает свои данные и DataContext установлен. Кажется, работает, но также, кажется, очень клуджи. :-( Спасибо за помощь, ребята!

0 голосов
/ 09 июля 2009

Если для заполнения данных требуется лишь долгое время, необходимо заполнить элемент управления в другом потоке, а затем добавить его через делегата:

Поскольку я не слишком хорош в написании VB.NET, но вот эквивалент C #:

private void Button1_Click(Object sender, RoutedEventArgs e)
{
  Thread thr = new Thread(delegate()
  {
    DataView dataView = new DataView();

    this.Dispatcher.BeginInvoke((Action) delegate()
    {
      teststack.Children.Add(dataView);
    });
  });
  thr.Start();
}
...