views:

812

answers:

1

I have a UserControl(a) with a stackpanel which has its ItemSource set to a collection. The StackPanel then contains a set of UserControl(b) that contain a few buttons and a datagrid control. Is there a way from the code behind in the UserControl(b) to access properties in the code behind of the parent UserControl(a).

Basically when UserControl(a) loaded into a window a parameter is passed in that contains whether the form will be considered read only or not. I would like bind the visibility of the buttons in Usercontrol(b) to the readonly property in the codebehind of the parent UserControl(a).

+1  A: 

Normally with WPF I'd suggest you implement the Model-View-ViewModel pattern (see MSDN).

With this pattern you'd create a ViewModel with all of the data in that you want to bind. This would be set as the data context for the (a) usercontrol. That control would then bind all of it's controls to properties on the datacontext.

The child (b) usercontrol would inherit this datacontext and could therefore bind it's controls to the same properties as (a) uses. This is because datacontexts are inherited down the logical (and visual) tree until such point as it's overridden.

So for you I'd be looking at creating a ViewModel that contains the property ReadOnly. You can then set this ViewModel object as the datacontext for the (a) usercontrol. The (b) usercontrol, since it's under the (a) usercontrol hierarchy will inherit the same datacontext. This will then allow you to bind controls within (b) to the same properties as (a) as shown below.

<Button IsEnabled="{Binding ReadOnly}"
    Context="Click me!"
    Command="{Binding ClickMeCommand}" />

To set the datacontext in the view code-behind I do something like this constructor shown below.

public MyView(IMyViewModel viewModel)
{
    InitializeComponent();
    DataContext = viewModel;
}

MyView is the class that inherits from UserControl in your instance. You don't have to get the viewmodel in the way I have, I'm using Unity to inject the viewmodel into the views that are constructed automatically since I'm using Prism but you can just create it as a normal object and assign it to the datacontext.

Note that I've also bound the command to the button using the datacontext as I usually expose those via the ViewModel too, this is easy if you create a wrapper class that implements ICommand and proxies to a delegate. See DelegateCommand blog article or look at the DelegateCommand class in Prism if you are interested.

If for some reason you do override the datacontext, which can happen when using a master/details view where you change the datacontext of the details section of the view to be the currently selected item in the list, then you can still access the parent datacontext by using a relative source binding.

E.g.

<ComboBox Grid.Row="1" Grid.Column="1" x:Name="Unit" IsReadOnly="True"
      ItemsSource="{Binding Path=DataContext.AvailableUnits, RelativeSource=
                   {RelativeSource Mode=FindAncestor,
                                   AncestorType={x:Type Window}}}"
      DisplayMemberPath="Name" SelectedItem="{Binding Unit}" />

Note the ItemsSource binding uses a relative source to find the parent window and then bind to a property of it's datacontext. I've also split the ItemsSource binding within the quotes across multiple lines for clarity here but don't do that in your xaml, I'm not sure it'll work there (not tried to see if markup extensions are that tolerant of whitespace).

RobG