This should be pretty easy, but it throws VS2008 for a serious loop.
I'm trying out WPF with MVVM, and am a total newbie at it although I've been developing for about 15 years, and have a comp. sci. degree. At the current client, I am required to use VB.Net.
I have renamed my own variables and removed some distractions in the code below, so please forgive me if it's not 100% syntactically perfect! You probably don't really need the code to understand the question, but I'm including it in case it helps.
I have a very simple MainView.xaml file:
<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>
I also have a UserControl called DataView that consists of a 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>
The constructor for the DataView usercontrol sets up the DataContext by binding it to a view model, as shown here:
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
The view model for DataView looks like this (there isn't much in 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
Now, this UserControl works just fine if I instantiate it in XAML. When I run the code, the grid shows up and populates it just fine.
However, the grid takes a long time to load its data, and I want to create this user control programmatically after the button click rather than declaratively instantiating the grid in XAML. I want to instantiate the user control, and insert it as a child of the StackPanel control:
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
When I do this, as soon as the Button1_Click finishes, my application locks up, starts eating RAM, and hits the CPU about 50%.
Am I not instantiating my UserControl properly? It all seems to come down to the DataContext assignment in DataEntry's constructor. If I comment that out, the app works as expected (without anything in the grid, of course).
If I move this code block into Button1_Click (basically moving DataEntry's constructor code up a level), the app still fails:
dataViewModel = New ViewModels.DataViewModel
dataViewModel.LoadDataEntries()
dataView.DataContext = dataViewModel
I'm stumped. Can anybody give me some tips on what I could be doing wrong, or even how to debug what infinite loop my app is getting itself into?
Many thanks.
The root cause of your issue appears to be either the raw amount of data you're loading or some inefficiency in how you load that data. Having said that, the reason you're seeing the application lock up is that you're locking the UI thread when loading the data.
I believe that in your first case the data loading has been off loaded onto another thread to load the data. In you second example you're instantiating the control on the UI thread and as a result all the constructor and loading logic is performed on the current thread (the UI thread). If you offload this work onto another thread then you should see similar results to the first example.
I eventually gave up on trying to get the DataContext on the UserControl set during instantiation of the UserControl (either in XAML or code). Now I load up the data and set the DataContext of the UserControl in an event in the UserControl (IsVisibleChanged, I believe). When I instantiate the UserControl in XAML, I have it's Visibility set to Hidden. When Button1 is clicked, I set the UserControl's Visibility to Visible. So the UserControl pops into view, and it loads up its data and DataContext is set. Seems to work, but also seems very kludgey. :-( Thanks for the help, folks!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With