0

I am creating a WPF Tabbed Interface utility program using MVVM Light. While free-standing dialog boxes live in their own .xaml files, my understanding was that to create tabbed interface panels, the code for those should reside in a DataTemplate within my MainWindow.xaml.

Everything was working fine until I needed to customize the appearance of the DataGrid. What I wanted was to call an event handler when a grid was created that would iterate through the columns and do some custom things for certain columns. So I added an Initialized event handler to the DataGrid. It only gets called when the first tab with a DataGrid is created... or when I close all the tabs and open a new first DataGrid tab. A little more investigation and I discover that DataContextChanged happens whenever I create a subsequent grid or click between tabs.

I'm thinking that I've committed a design flaw, that while there is a separate view-model for each grid, there is only a single, common grid resource, but am unsure how to correct it.

My Question: How do I go about creating a completely separate grid for each tab?

Sample View with several tabs open

DataTemplate from MainWindow.xaml:

<Window...>
    <Window.Resources>
    <DataTemplate DataType="{x:Type vm:GridViewModel}">
        <Grid>
            <DataGrid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,32" RowHeight="{Binding RowHeight}" SelectionUnit="Cell" 
                      IsReadOnly="True" LoadingRow="DataGrid_LoadingRow" ItemsSource="{Binding RecordSet}" AutoGenerateColumns="false" 
                      Initialized="DataGrid_Initialized" DataContextChanged="DataGrid_DataContextChanged" >

            </DataGrid>
        </Grid>
    </DataTemplate>
    </Window.Resources>
</Window>

TabControl from within Grid from MainWindow.xml

<TabControl x:Name="DocumentArea" ItemsSource="{Binding Workspaces}" SelectedIndex="{Binding SelectedIndex}" HorizontalAlignment="Stretch" Margin="0,35,0,35" VerticalAlignment="Stretch">
    <TabControl.ItemTemplate>
        <DataTemplate>
            <WrapPanel>
                <TextBlock Text="{Binding Header}" />
                <Button Command="{Binding CloseCommand}" Content="X" Margin="4,0,0,0" FontFamily="Courier New" Width="17" Height="17" VerticalContentAlignment="Center" />
            </WrapPanel>
        </DataTemplate>
    </TabControl.ItemTemplate>
</TabControl>

Example of a method in MainViewModel.cs that creates a Grid:

private void ShowAccountsGrid()
{
    GridViewModel viewModel = new GridViewModel { Header = "QuickBooks Chart of Accounts" };

    Workspaces.Add(viewModel);
    SelectedIndex = Workspaces.IndexOf(viewModel);
    viewModel.RecordSet = _quickBooksHelper.AccountList;
}

Relevant code from GridViewModel.cs:

public class GridViewModel : WorkspaceViewModel
{
    private readonly IDataService _dataService;

    public RelayCommand DoDeleteRow { get; set; }
    public RelayCommand DoShowRowDetail { get; set; }

    [PreferredConstructor]
    public GridViewModel(IDataService dataService) :base()
    {
        InitBoundFields();

        SetupRelayCommands();
        _dataService = dataService;
        _dataService.GetData(
            (item, error) =>
            {
                if (error != null)
                {
                    // Report error here
                    return;
                }
            });
    }
Bob Kaufman
  • 12,864
  • 16
  • 78
  • 107
  • Nothing wrong with your code from what I can see, you'll have to show the rest of the XAML in MainWindow.xaml that's using the template. Needs to be pointed out though your use of code-behind (`Initialized`, `DataContextChanged` etc) means this isn't MVVM, and that's quite likely the source of your problem. – Mark Feldman Aug 12 '18 at 22:46
  • BTW problems of this nature are typically solved by putting `x:Shared="False"` in the DataTemplate tag. But again, it's usually a band-aid solution for a more fundamental underlying problem. – Mark Feldman Aug 12 '18 at 22:52
  • @MarkFeldman `x:Shared="False"` had no effect. There is no other XAML in `MainWindow` that refers to this `DataTemplate`. Your comments suggest that I'm missing some key MVVM concepts; this is my first attempt at using it. I will do some learning which will hopefully make my coding error more obvious to me. – Bob Kaufman Aug 13 '18 at 01:05
  • The code you posted is from your `Window.Resources` block, but resources don't display anything in-and-of themselves. There has to be something else in your XAML that references that DataTemple, otherwise you wouldn't see the grid at all. In this case the reference will be implicit, by virtue of the fact that your DataTemplate is using DataType instead of a key, but it's still there. What's the XAML for the code that is actually using that template? – Mark Feldman Aug 13 '18 at 01:10
  • What do you mean by "tabbled interface"? Are you using the TabControl? Please post your XAML markup. – mm8 Aug 13 '18 at 11:23
  • @mm8 - added `TabControl` markup from `MainWindow.xml`. @MarkFeldman - your request will take a little longer. I'll post this evening. Thank you. – Bob Kaufman Aug 13 '18 at 12:39

0 Answers0