问题
I'm about to create a new Expander Control (learning purpose) by creating different templates but can't figure out what I'm doing wrong...
ToggleButtonTemplate:
<ToggleButton>
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="eBB" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Path x:Name="Sign" Data="M 0,10 L 7.5,2.5 L 15, 10" Stroke="Black" Width="15">
<Path.RenderTransform>
<RotateTransform Angle="0"/>
</Path.RenderTransform>
</Path>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Data" TargetName="Sign" Value="M 0,2.5 L 7.5,10 L 15,2.5"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Stroke" Value="#222" TargetName="Sign"/>
<Setter Property="Background" Value="#666" TargetName="eBB"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Stroke" Value="#FF003366" TargetName="Sign"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
Expander Template:
<Expander>
<Expander.Template>
<ControlTemplate TargetType="Expander">
<Grid>
<Grid.RowDefinitions>
<RowDefinition x:Name="ContentRow" Height="*"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Visibility="Collapsed" Content="{TemplateBinding Content}"/>
<local:FullSizeExpanderToggleButton Grid.Row="1" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Height" Value="*" TargetName="ContentRow"/>
</Trigger>
<Trigger Property="IsExpanded" Value="False">
<Setter Property="Height" Value="0" TargetName="ContentRow"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Expander.Template>
</Expander>
Now when I want to add the Expander in my Main View:
<custom:FullSizeExpander Width="300">
<Button/>
</custom:FullSizeExpander>
the whole space inside the Control gets filled by the Button (the ToggleButton isn't visible anymore).
What am i doing wrong?
In addition I have some questions regarding this issue:
- What does "ContentSource="Content"" do? What is it for? Whats different to "Content="{Templatebinding Content}""?
- Does the Expander's Property "IsExpanded" get changed when the ToggleButtons Property "IsPressed" gets changed? What if there is no Togglebutton in the Expander at all?
回答1:
first off, consider modifying your Expander template to look something like this:
<Expander>
<Rectangle Height="500" Width="500" Fill="Red"/>
<Expander.Template>
<ControlTemplate TargetType="Expander">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" x:Name="ContentPresenter"/>
<ToggleButton Grid.Row="1" IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsExpanded}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="ContentPresenter" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsExpanded" Value="False">
<Setter TargetName="ContentPresenter" Property="Visibility" Value="Collapsed"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Expander.Template>
</Expander>
I'll explain how it works, and why it wasn't working before from the top-down. First off, you'll want to actually put something in the expander to make sure it's working - i put a rectangle here with fixed sizes for now.
Next, i changed the first RowDefinition to be auto instead of *, as you want the expander to actually expand when opened. (rather than just hide its content in a big empty area). Auto uses exactly as much space as the content in the row needs, so when it's collapsed, that size will be 0, and when it's expanded, auto will become 500 to fit the rectangle.
The third thing i did was remove your bindings from the ContentPresenter. As it happens, Windows' content-bearing templates (as in anything that can have something else placed inside of it) will automatically look for the first ContentPresenter / ItemsPresenter tag inside its template and shove content into it.
As for the togglebutton however (i kept it simple and left it as a standard togglebutton), this one does actually need a binding. What i did was a Relativesource Templatebinding to the property "IsExpanded". Togglebuttons have 2 main states: "Checked" and "Unchecked" (true/false), and Expanders have 2 main states: "Expanded" and "Collapsed" (true/false). So essentially all i did was tell the ToggleButton to share its true/false state of being checked or unchecked with the parent it sits inside of.
The full binding again is "{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsChecked}"
, which in english is essentially saying "Bind to a related source, and the related source is the parent you're in the template of, and bind so to said template;s "IsChecked" property.
Lastly i changed your triggers which were going the long way around to get the ContentPresenter to become hidden (trying to squash it by reducing the size of the Grid.Row it sits in), and instead just told it to hide when the expander's "IsExpanded" (and thanks to our binding, the ToggleButton's "IsChecked") is set to false, and the opposite when they're set to true.
.
As for your other questions: 1) The ContentSource is used to give the ContentPresenter an alias/alternate name, and i doubt you'll need it anytime soon. The property name is sort of misleading, i grant you.
2) As we saw above, no - the ToggleButton needs to be bound to the templated parent's "IsExpanded" property in order to work. If you were to take the button out, the Expander simply would not work until you created a binding or made an instruction in code to tell it to open/close.
来源:https://stackoverflow.com/questions/34879748/wpf-expander-templating-display-content-above-togglebutton