I am trying to create a very flexible custom control. The flexibility that I'm trying to achieve to be able to bind UserControl
to the ExpanderContent
Dependency
property, code behind snippet:
public partial class ChartBar : UserControl
{
public UIElement ExpanderContent
{
get { return (UIElement)GetValue(ExpanderContentProperty); }
set { SetValue(ExpanderContentProperty, value); }
}
// Using a DependencyProperty as the backing store for ExpanderContent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ExpanderContentProperty =
DependencyProperty.Register("ExpanderContent", typeof(UIElement), typeof(ChartBar), new PropertyMetadata(null, OnExpanderContentChanged));
private static void OnExpanderContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//throw new NotImplementedException();
}
.
.
.
I have tried using a ContentPresenter
in the XAML but it doesn't work. I obviously can fill with buttons and it works but this defeats the dynamic content via binding.
<Expander x:Name="expander" Header="" VerticalAlignment="Top" d:LayoutOverrides="Width" Style="{DynamicResource ExpanderStyle1}">
<ContentPresenter Content="{Binding ExpanderContent, ElementName=TestControlWithContent}" />
<!--<WrapPanel HorizontalAlignment="Center" >
<Button Content="A" Style="{DynamicResource ButtonStyle1}" />
<Button Content="B" Style="{DynamicResource ButtonStyle1}" />
<Button Content="C" Style="{DynamicResource ButtonStyle1}" />
<Button Content="D" Style="{DynamicResource ButtonStyle1}" />
<Button Content="E" Style="{DynamicResource ButtonStyle1}" />
<Button Content="F" Style="{DynamicResource ButtonStyle1}" />
</WrapPanel>-->
</Expander>
What's even more confusing is I can do
// ChartBarParent is the name of the custom control set in XAML
ChartBarParent.Content = new TestControlWithContent();
and it works as well as fires the callback.
Ultimately, is UIElement
in a dependency property and using a ContentPresenter
the right way to do this?
Try changing the ContentPresenter to a ContentControl.
Furthermore, you could wrap the UserControl in a DataTemplate and set it as the ContentControl.ContentTemplate allowing you to flow data context through the ContentControl.Content property.
Here's how I would do this. It relies on the SecondaryContent
being either UI stuff (like the second example in "Usage" below), or else a viewmodel with an implicit DataTemplate
. I could easily add a SecondaryDataTemplateSelector
property to give the consumer more explicit control over how that templating happens.
ChartBar.cs
public class ChartBar : ContentControl
{
static ChartBar()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ChartBar),
new FrameworkPropertyMetadata(typeof(ChartBar)));
}
// Rather than ExpanderContent, we're inheriting ContentControl.Content for the
// main control content.
#region SecondaryContent Property
public Object SecondaryContent
{
get { return (Object)GetValue(SecondaryContentProperty); }
set { SetValue(SecondaryContentProperty, value); }
}
public static readonly DependencyProperty SecondaryContentProperty =
DependencyProperty.Register("SecondaryContent", typeof(Object), typeof(ChartBar),
new PropertyMetadata(null));
#endregion SecondaryContent Property
#region IsExpanded Property
// This is optional. I just know I'd end up wanting it.
public bool IsExpanded
{
get { return (bool)GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}
public static readonly DependencyProperty IsExpandedProperty =
DependencyProperty.Register("IsExpanded", typeof(bool), typeof(ChartBar),
new PropertyMetadata(false));
#endregion IsExpanded Property
}
Themes/Generic.xaml
, or else App.xaml, within <Application.Resources>
, or some other .xaml resource dictionary included in one or the other.
<ControlTemplate x:Key="ChartBarDefaultTemplate" TargetType="local:ChartBar">
<!--
Use Binding/RelativeSource TemplatedParent on IsExpanded so it updates both ways,
or remove that attribute/binding if you're not bothering with the IsExpanded DP.
-->
<Expander
x:Name="expander"
Header=""
VerticalAlignment="Top"
Style="{DynamicResource ExpanderStyle1}"
IsExpanded="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
>
<StackPanel Orientation="Vertical">
<ContentPresenter />
<ContentControl
Content="{TemplateBinding SecondaryContent}"
/>
</StackPanel>
</Expander>
</ControlTemplate>
<Style TargetType="local:ChartBar">
<Setter
Property="Template"
Value="{StaticResource ChartBarDefaultTemplate}"
/>
</Style>
Usage:
<StackPanel Orientation="Vertical" >
<!-- Control content -->
<local:ChartBar SecondaryContent="Secondary Content One">
<StackPanel Orientation="Vertical">
<Label>Chart Bar</Label>
<Ellipse Height="30" Width="60" Fill="GreenYellow" Opacity="0.2" />
<Label>Other stuff, etc. etc.</Label>
</StackPanel>
</local:ChartBar>
<!-- Templated viewmodel content -->
<local:ChartBar Content="{Binding RandomViewModelProperty}" IsExpanded="True">
<local:ChartBar.ContentTemplate>
<DataTemplate>
<Label Background="Beige" Content="{Binding}" Margin="20" />
</DataTemplate>
</local:ChartBar.ContentTemplate>
<local:ChartBar.SecondaryContent>
<ComboBox>
<TextBlock Text="One" />
<TextBlock Text="Two" />
<TextBlock Text="Three" />
</ComboBox>
</local:ChartBar.SecondaryContent>
</local:ChartBar>
</StackPanel>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.