1

In my application I'd like to have a drop-down box to choose a table to edit (of about 20). Each table should be represented by its own WPF DataGrid. (I'd thought about using a single DataGrid and creating a set of new columns at runtime with code-behind, but that doesn't seem very XAML-ish.)

My drop-down is in a UserControl (since it's part of a larger application). I believe (from my research) that the placeholder for the one-of-20 DataGrids should be a ContentControl used as a placeholder here:

<UserControl x:Class="MyClass" ...
         xmlns:my="clr-namespace:MyNamespace"
         DataContext="{Binding ViewModel}">
<StackPanel>
    <Grid>
        <ComboBox Name="DataPaneComboBox" HorizontalAlignment="Stretch" 
                  IsReadOnly="True" MinWidth="120" 
                  Focusable="False" SelectedIndex="0"
                  DockPanel.Dock="Left" Grid.Column="0"
                  SelectionChanged="DataPaneComboBox_SelectionChanged">
            <ComboBoxItem Name="FirstOption" Content="Choice 1" />
            <ComboBoxItem Name="SecondOption" Content="Choice 2" />
            <ComboBoxItem Name="ThirdOption" Content="Choice 3" />
        </ComboBox>
    </Grid>
    <ContentControl Name="DataGridView" Margin="0,3,0,3" Content="{Binding CurrentView}" />
</StackPanel>

Here's my code-behind for this class:

public partial class MyClass : UserControl {
    private MyViewModel ViewModel {
        get; set;
    }

    public MyClass() {
        InitializeComponent();
        ViewModel = new MyViewModel();
        ViewModel.CurrentView = new DataGridChoice1();
    }
}

And the ViewModel (class ObservableObject implements the INotifyPropertyChanged interface):

public class MyViewModel : ObservableObject {
    private UserControl _currentView;

    public UserControl CurrentView {
        get {
            if (this._currentView == null) {
                this._currentView = new DatGridChoice1();
            }

            return this._currentView;
        }
        set {
            this._currentView = value;
            RaisePropertyChanged("CurrentView");
        }
    }
    #endregion
}

And one of the 20 or so UserControls that can be substituted in at runtime:

<UserControl x:Class="Choice1Control"
             xmlns:my="clr-namespace:MyNamespace">
    <DataGrid ItemsSource="{Binding Choice1Objects}" />
        <!-- ... -->
    </DataGrid>
</UserControl>

When the user changes the drop-down I'd like the program to load the appropriate DataGrid. Right now I can't see the child UserControl (here, Choice1Control). I've added the child directly (without the intervening ContentControl) and it works fine.

I've tried just about every combination of DataContext and UserControl content binding. I'm new to WPF, so I'm probably missing something glaringly obvious. Thanks!

James Cronen
  • 5,715
  • 2
  • 32
  • 52
  • where are you assigning datasource in your code behind i.e for the ViewSource..etc...?? – MethodMan Feb 15 '12 at 21:53
  • What do you mean? I was under the impression that setting the DataContext as class `ViewModel` and setting the ContentControl's Content to point to `CurrentView` is enough to indicate where the control is specified. Can you be more specific? – James Cronen Feb 15 '12 at 22:04
  • I was talking more in the area of Binding.. I see you are using the {Binding } but Binding requires some sort of Source – MethodMan Feb 15 '12 at 22:05
  • So then what should `Content="{Binding CurrentView}"` be? I'm not binding to a Resource or anything within the XAML. (And just for kicks I tried `Content="{Binding RelativeSource={RelativeSource Self}, Path=CurrentView}"` and that seemed to have no effect.) – James Cronen Feb 15 '12 at 22:10
  • If what I have posted does not make sense to you I will edit / remove it and improve on my example to help lead you in a better direction... thanks – MethodMan Feb 15 '12 at 22:10
  • Choice1Objects is in the ViewModel of the one-of-twenty DataGrid UserControls; again, by just referencing Choice1Control directly in the XAML it displays properly. The issue is getting it to appear when invoked programmatically. – James Cronen Feb 15 '12 at 22:24
  • I think that this link will help you I still think that in code behind you have to specify your listview or DataGridviews.ItemSource = to something.. http://stackoverflow.com/questions/1633800/wpf-datagrid-datagridcomboxbox-itemssource-binding-to-a-collection-of-collectio – MethodMan Feb 15 '12 at 22:28

2 Answers2

1

Path needs a Source to go against (Source, DataContext, RelativeSource, ElementName). ElementName can only be used to refer to elements declared in XAML by their x:Name.

MethodMan
  • 18,625
  • 6
  • 34
  • 52
  • Doesn't specifying a Binding RelativeSource effectively override the DataContext? What's wrong with what I did: specifying the DataContext as the attached ViewModel? My impression of your example is that it will use the DataContext of the parent Window, which is the top level of my application and will have a different DataContext than would be used here. For fun I also tried it with `{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=CurrentView}` but the same result. :-( – James Cronen Feb 15 '12 at 22:18
  • and if you try this – MethodMan Feb 15 '12 at 22:22
  • That's not the problem. As a test I replaced my `` with `` and the Choice1Control appears as expected. The DataContext and Binding for the DataGrid itself is ok; it's the DataContext/Binding attaching the child DataGrid to the parent UserControl that is misbehaving. (Thanks for your ongoing help, btw.) – James Cronen Feb 15 '12 at 22:29
  • Ahha..now I understand ..I thought it was the other way around.. it's late in the day.. and I've been at this Q&A all day.. I apologize... You may have to also look at creating new Binding for every Child DataGrid.. – MethodMan Feb 15 '12 at 22:30
  • No worries, I appreciate the attempt! – James Cronen Feb 15 '12 at 22:31
0

For some reason it never occurred to me that there would be Binding errors written clearly in the log at run time. I futzed around until I actually got a useful message and could get to the root of the problem.

It seems that the DataContext of the root UserControl was getting intercepted before it could be inherited by the ContentControl. (Or, my impression of how DataContext is inherited/propagated is wrong.)

In the end I changed the MyClass constructor to explicitly specify the DataContext as the ViewModel.

public MyClass() {
    InitializeComponent();
    ViewModel = new MyViewModel();
    ViewModel.CurrentView = new DataGridChoice1();            
    this.DataContext = ViewModel;                      // <-- Added this line
}

Bindings then work as expected, and I'm able to change between multiple DataGrids as the drop-down box changes state.

I'd love to hear why the initial binding was wrong. However, I'll claim a small victory now and leave that conundrum for another day.

James Cronen
  • 5,715
  • 2
  • 32
  • 52