Reputation: 96
I have a UserControl called PAInstructionsControl inside my TestAdmin Class Library.
<UserControl x:Class="TestAdmin.Controls.PAInstructionsControl">
<Button Command="{Binding ???}"
CommandParameter="{Binding ???}"/>
</UserControl>
In the consumers of this UserControl: I need a ViewModel used as the Button's Command Binding and a different ViewModel for its CommandParameter but I'm not sure how to do this.
In my WMT Class Library I have a WmtPAInstructionsView UserControl that consumes the PAInstructionsControl
<UserControl x:Class="WMT.View.WmtPAInstructionsView"
xmlns:controls="clr-namespace:TestAdmin.Controls;assembly=TestAdmin">
<StackPanel>
<TextBlock Text="WMT PA Instructions View"
Margin="10"/>
<controls:PAInstructionsControl />
</StackPanel>
</UserControl>
The WmtPAInstructionsView is nested inside a WMT.WmtMainWindow UserControl
If the Button was simply inside the WmtPAInstructionsView UserControl, then I would need these ViewModel bindings :
<Button
Command="{Binding DataContext.DisplayNextIndexedViewCommand, RelativeSource=
{RelativeSource AncestorType={x:Type local:WmtMainWindow}}, Mode=OneWay}"
CommandParameter="{Binding}"/>
So the command would bind to a command within the WmtMainWindow's DataContext. The CommandParameter would be the WmtPAInstructionView's DataContext which is the WmtPAInstructionsViewModel
But I need the button in the PAInstructionsControl so it can be re-used elsewhere in my solution. What would I use for the bindings of the Command and CommandParameter? Do I need Dependency Properties?
Hopefully the above information is all you need to help answer my question. Here is more information if you need it:
I also have a MSVT Class Library that has a MsvtPAInstructionsView UserControl that consumes the PAInstructionsControl in a similar way.
If the Button was simply inside the MsvtPAInstructionsView UserControl, then I would need these ViewModel bindings :
<Button
Command="{Binding DataContext.DisplayNextIndexedViewCommand, RelativeSource=
{RelativeSource AncestorType={x:Type local:MsvtMainWindow}}, Mode=OneWay}"
CommandParameter="{Binding}"/>
Both the WMT and MSVT Class Libraries reference the TestAdmin library. WMT and MVST do NOT reference each other.
Here is the Main Application Window (WpfTestBase.MainWindow):
Clicking on "New WMT Test" opens a new WpfTestBase.TestView Window. This TestView Window has several nested user controls :
Here's the Visual Tree:
Upvotes: 0
Views: 224
Reputation: 6724
The "easy" (and sort of bad) solution would be to do this:
<Button
Command="{Binding DataContext.DisplayNextIndexedViewCommand, RelativeSource=
{RelativeSource AncestorType={x:Type Window}}, Mode=OneWay}"
CommandParameter="{Binding}"/>
This would work for any subclass of Window
. While it does "solve" your problem, it makes a number of assumptions. The biggest is that you'd be assuming you'll always want your Button
to bind to the parent Window
, and that that Window
's DataContext
will always have a property exactly named DisplayNextIndexedViewCommand
.
A better solution would be to define a chain of DependencyProperty
s in each of your UserControl
s, where you would bind each child to its parent. This would give you total flexibility when consuming the controls.
Start with PAInstructionsControl
and define a DisplayNextIndexedViewCommand
DependencyProperty
. You would then bind Buton.Command
to that property like so:
<Button
Command="{Binding DisplayNextIndexedViewCommand, RelativeSource=
{RelativeSource AncestorType={x:Type local:PAInstructionsControl}}, Mode=OneWay}"
CommandParameter="{Binding}"/>
This brings you one level of nesting closer to the top. Next, also declare a DisplayNextIndexedViewCommand
DependencyProperty
in WmtPAInstructionsView
. This lets you bind PAInstructionsControl.DisplayNextIndexedViewCommand
to WmtPAInstructionsView.DisplayNextIndexedViewCommand
like so:
<UserControl x:Class="WMT.View.WmtPAInstructionsView"
xmlns:controls="clr-namespace:TestAdmin.Controls;assembly=TestAdmin">
<StackPanel>
<TextBlock Text="WMT PA Instructions View"
Margin="10"/>
<controls:PAInstructionsControl DisplayNextIndexedViewCommand="{Binding DisplayNextIndexedViewCommand, RelativeSource={RelativeSource AncestorType={x:Type local:WmtPAInstructionsView}}}"/>
</StackPanel>
</UserControl>
Now, finally, you can bind WmtPAInstructionsView.DisplayNextIndexedViewCommand
to the actual command, which you have living in your Window
's DataContext
:
<local:WmtPAInstructionsView DisplayNextIndexedViewCommand="{Binding DataContext.DisplayNextIndexedViewCommand, RelativeSource={RelativeSource AncestorType={x:Type local:WmtMainWindow}}, Mode=OneWay}"/>
(Also, since DataContext
is inherited, you might be able to leave out "DataContext.
" and the whole RelativeSource
from the Binding
)
Upvotes: 1