I have a requirement where a where user can switch to view hierarchical data either as tree or as a text in datagrid or as FlowChart.
The user can do this by clicki
H.B.'s answer is good, but there are scenarios where it's not quite so good.
If constructing the views (and their underlying view models) is expensive, then toggling the Content
property will pay this expense every time the user changes the view.
In some scenarios, it makes sense to create both views in the same container (e.g. a Grid
), and toggle their Visibility
instead. If you use lazy evaluation in your view models, the expensive operations won't be conducted until the view becomes visible, and - importantly - they'll only be conducted the first time the view becomes visible. Once both views have been displayed, the user can toggle back and forth between views without reconstructing the underlying view models.
Edit:
I stand corrected, sort of: H.B.'s answer is not as good as it looked .
You can't use a style to set the Content
property of a ContentControl
to a UIElement
. See this blog post for full details, but the long and short of it is that if you use H.B.'s approach, you'll get a runtime error.
You can set the ContentTemplate
property, instead, e.g.:
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewMode}"
Value="TreeMode">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<uc:TreeModeView/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ViewMode}"
Value="GridMode">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<uc:GridModeView/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
If the state of which view to show is saved in some enum property you could use a ContentControl and DataTriggers for example:
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewMode}" Value="TreeMode">
<Setter Property="Content">
<Setter.Value>
<uc:TreeModeView />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ViewMode}" Value="GridMode">
<Setter Property="Content">
<Setter.Value>
<uc:GridModeView />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
(As the style is only used in one place, by setting it directly as ContentControl.Style
this will work, if you want to use it in more than one place you should set the ContentTemplate
instead, because otherwise there will only be one view instance shared by all controls with the style which is not allowed by WPF (of course Content
needs to be set to something for the template to be applied))
You could also bind directly to IsChecked
of the ToggleButton
using ElementName of course. The relevant values would then be True
, False
and {x:Null}
.