I'm developing an MVVM WPF application and have run across another small stumbling block. In my main application window, I'm using a tabbed control as my MDI. This tab control is bound to a (observable) collection named OpenWorkspaces, which contains all the "windows" (or Workspaces in my app's language) the user has open. Here is what a "Workspace" looks like:
Public Class Workspace Private _WorkspaceID As Integer Public Property WorkspaceID() As Integer Get Return _WorkspaceID End Get Set(ByVal value As Integer) _WorkspaceID = value End Set End Property Private _WorkspaceName As String Property WorkspaceName As String Get Return _WorkspaceName End Get Set(value As String) _WorkspaceName = value End Set End Property Private _ViewModelName As String Property ViewModelName As String Get Return _ViewModelName End Get Set(value As String) _ViewModelName = value End Set End Property Private _ViewObjectName As String Public Property ViewObjectName() As String Get Return _ViewObjectName End Get Set(ByVal value As String) _ViewObjectName = value End Set End Property Private _ViewObject As UserControl Property ViewObject As UserControl Get Return _ViewObject End Get Set(value As UserControl) _ViewObject = value End Set End Property Private _CanSupportMultipleInstances As Boolean Property CanSupportMultipleInstances As Boolean Get Return _CanSupportMultipleInstances End Get Set(value As Boolean) _CanSupportMultipleInstances = value End Set End Property Private _CanOpenDisconnectedFromDB As Boolean Public Property CanOpenDisconnectedFromDB() As Boolean Get Return _CanOpenDisconnectedFromDB End Get Set(ByVal value As Boolean) _CanOpenDisconnectedFromDB = value End Set End Property End Class
Here is the Xaml for my TabControl:
<TabControl ItemsSource="{Binding OpenWorkspaces}" SelectedItem="{Binding SelectedTab, Mode=TwoWay}" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="2" BorderThickness="0" BorderBrush="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"><TabControl.ItemTemplate><DataTemplate><Grid><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition Width="10"/><ColumnDefinition/></Grid.ColumnDefinitions><TextBlock Text="{Binding WorkspaceName}" Grid.Column="0" Padding="0,1,0,0"/><Button Content="X" Grid.Column="2" Command="{Binding DataContext.CloseWorkspace, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Mode=TwoWay}" CommandParameter="{Binding}" BorderThickness="0" BorderBrush="Transparent" Background="Transparent" Padding="-4"/></Grid></DataTemplate></TabControl.ItemTemplate><TabControl.ContentTemplate><DataTemplate><UserControl Content="{Binding ViewObject}"/></DataTemplate></TabControl.ContentTemplate></TabControl>
Here is my Execute (from the ICommand) routine bound to my left hand menu buttons (one for each Workspace the user has permission to) which opens a Workspace on Click:
Public Sub ExecOpenWorkspace(ByVal inWorkspaceName As String) 'Declare new Workspace variable Dim objWorkspace As New Workspace Dim exCreatingWorkspace As New CustomException 'Check to see if this Workspace exists in _OpenWorkspaces If CheckForOpenWorkspace(inWorkspaceName) = False Then 'Attempt to create new workspace and check for an error If CreateWorkspaceByName(inWorkspaceName, objWorkspace, exCreatingWorkspace) = 0 Then 'Error occurred... set consequence and inform user of error exCreatingWorkspace.ExceptionConsequence = "Unable to launch form " & inWorkspaceName exCreatingWorkspace.ShowErrorMessage() Else 'Add inWorkspace to _OpenWorkspaces _OpenWorkspaces.Add(objWorkspace) 'Set focus to new tab '??? End If Else 'Attempt to create the Workspace and check return value If CreateWorkspaceByName(inWorkspaceName, objWorkspace, exCreatingWorkspace) = 0 Then 'Error occurred... set consequence and show Error exCreatingWorkspace.ExceptionConsequence = "Unable to launch form " & inWorkspaceName exCreatingWorkspace.ShowErrorMessage() Else 'Need to check to see if this workspace can have multiple instances 'open at the same time. If objWorkspace.CanSupportMultipleInstances = True Then 'Add the workspace to OpenWorkspaces collection _OpenWorkspaces.Add(objWorkspace) 'Set focus to new tab '??? Else 'Set Focus to the existing Tab '??? Debug.Print(_SelectedTab.Header.text) End If End If End If End Sub
I'm not certain if this is relevant to this problem, but here is the code for CreateWorkspaceByName:
Public Function CreateWorkspaceByName(inWorkspaceName As String, ByRef outWorkspace As Workspace, _ ByRef outException As CustomException) As Integer 'This routine will attempt to create a new Workspace correlated to the passed in 'WorkspaceName. 'The routine will return 1 if it runs successfully, 0 if any error occurs, and will 'also pass back a workspace object through outWorkspace when successful. 'Declare variables Dim objTempWorkspace As New Workspace Dim objViewObject As New Object Dim objViewType As Type Dim exConnecting As New CustomException Try 'Use LINQ to retrieve the passed in Workspace by Name Dim objWorkspace = (From WorkSpace In _AvailableWorkspaces Where WorkSpace.WorkspaceName = inWorkspaceName Select WorkSpace).SingleOrDefault 'Attempt to connect to the database. Since the Property/Field CanOpenDisconnectedFromDB 'determines whether a Workspace can be opened without a database connection, we should 'attempt to connect now before we bother to go through with creating the workspace. 'Attempt to connect and check for error If viewModelMainWindow.CurrentUser.EstablishUserConnection(exConnecting) = 0 Then 'No Connection. Need to check CanOpenDisconnectedFromDB If objWorkspace.CanOpenDisconnectedFromDB = False Then 'Set outException to exConnecting outException = exConnecting 'Return 0 Return 0 End If End If 'I want to create a temporary Workspace object to pass back because I believe that if 'I create a ViewObject (View) for objWorkspace that it will actually store that view 'with the Workspace objects in _AvailableWorkspaces. I don't want that to happen... 'I want the application to close and recreate the ViewObject every time the user 'closes or opens a form and I also don't want the memory overload of my menu class objects 'storing Views. objTempWorkspace.WorkspaceID = objWorkspace.WorkspaceID objTempWorkspace.WorkspaceName = objWorkspace.WorkspaceName objTempWorkspace.ViewObjectName = objWorkspace.ViewObjectName objTempWorkspace.ViewModelName = objWorkspace.ViewModelName 'Use GetType to cast object as new View from ViewObjectName field objViewType = Type.GetType("CoreTimeClock." & objTempWorkspace.ViewObjectName, True, True) objViewObject = Activator.CreateInstance(objViewType) 'Add objViewObject to objWorkspace objTempWorkspace.ViewObject = objViewObject 'Return objTempWorkspace outWorkspace = objTempWorkspace 'Return 1 to indicate success CreateWorkspaceByName = 1 Catch ex As Exception 'Return 0 to indicate failure CreateWorkspaceByName = 0 'Set CustomException properties outException.IsCustomException = False outException.ExceptionObject = ex outException.AttemptedAction = "attempting to create form " & inWorkspaceName & "." outException.GeneratedBy = "viewModelMainWindow" outException.GeneratedByRoutine = "CreateWorkSpaceByName" 'Log Error outException.LogError() End Try End Function
So, if you look at ExecOpenWorkspace, my comments indicate what my problem is. When I create a new Workspace (which is a TabItem on my TabControl), it gets created just fine, but doesn't get "focus". Also, I want the result of clicking a button for a Workspace (which can only be opened once) that is already opened to be for it to receive "focus". On a different form, I was able to do "on demand loading" of the individual tabs of a tab control by binding to "SelectedItem". However, that TabControl isn't dynamic like the one for my main window, which I think makes a big difference because "SelectedItem" always seems to be Nothing. In other words... my "Debug.Print" line always generates an exception.
Any ideas on how to accomplish setting focus with my dynamic tab control? As always, thanks in advance for the help! The guidance I've received from the MSDN community has been absolutely invaluable :).