HandyControl
HandyControl copied to clipboard
Do not use the Style property to provide default appearance, use DefaultStyleKey instead.
You should use the DefaultStyle
property to provide default appearance and let user to use the Style
property to modify it.
Now you occupied the Style
property, so we can only use a style with a BasedOn
. Without it, the new style with few setters will replace the original style and makes the control invisible.
Why not set the DefaultStyleKey
property like normal custom controls? Using the DefaultStyleKey
can also help the control display normally even without adding the HandyControl's resource dictionary.
It is a problem to have 2 Style properties. What style setters have the priority on the others ?
- If the
Style
setter properties have the priority : in that case, It goes the same way that theBasedOn
. (And you have the same problem) - If the
DefaultStyleKey
setter properties have the priority : Here , you will be able to modify only the setter that are not in theDefaultStyleKey
You can find here the NumericUpDownBaseStyle.xaml
If you give a little of context, me or someone else should be able to help you.
You can create a new project using HandyControl and add a NumericUpDown
to the window. Then set its Style
property to null: Style="{x:Null}"
, it will disappear.
Then add a CheckBox
to the window and set itsStyle
property to null. It will not disappear. See the difference?
CheckBox
uses DefaultStyle
or DefaultStyleKey
to provide the default appearence, so setting the Style
to null will not affect it. The Style
setter properties definitely have the priority. But if I set an empty style or null to Style
, nothing should happen.
- you said 'so we can only use a style with a BasedOn', it's wrong, for instance:
<Style x:Key="NumericUpDownExtend" TargetType="hc:NumericUpDown">
...
</Style>
It will works without BasedOn
- you also said 'Using the DefaultStyleKey can also help the control display normally even without adding the HandyControl's resource dictionary', it's only half right, we also need generic.xaml, yes, we can put hc default styles in it, make styles invalid without adding resource dictionary, while for native controls, we can not put detault styles in generic.xaml, for instance, try this style without key:
<Style TargetType="Button"/>
thus, we can only divide styles into HC and native, that means you have to refer to the resource dictionary. Instead, it's better not to divide, just put them all together.
-
Define a style with key:
<Style x:Key="NumericUpDownExtend" TargetType="hc:NumericUpDown"/>
and then apply to a NumericUpDown:<hc:NumericUpDown Style="{StaticResource NumericUpDownExtend}"/>
, it will disappear, too. Meanwhile, doing the same thing to CheckBox will not make it disappear. -
According to the official document or magazine, you must use
DefaultStyleKey
When you put a ControlTemplate in any of the theme-specific resource dictionary files, you must create a static constructor for your control and call the OverrideMetadata(Type, PropertyMetadata) method on the DefaultStyleKey.
Also, most times I just want to use some controls in HandyControl, not apply your skin to my whole project. So you can put your style of native controls to generic.xaml
without key if you use DefaultStyleKey
. Then if I just want to use some controls, I can only add the control, without referencing the generic.xaml
. If I want to apply your skin to my whole window or project, I can reference the generic.xaml
. Also this is a simpler way to solve this issue.
@ghost1372 Any ideas?
Heyπ , @NaBian @ghost1372 @akimusume
I think I just found why this happen π ! I got a similar problem by creating new control using the same code structure as Handy !
The fact is that Handy create a "Template" for a control in a Style
. So if you make it {x:Null}
you will break the Template and found the default style of WPF control on which the control inherit (not sure of the formulation there π
).
π The only link of the Template of a new Control and the Control is the Style
property.
If I take an example : hc:CheckComboBox
If I give {x:Null}
to the style property : i found a listbox with the basic wpf style. (because checkcobobox inherit listbox)
CheckComboBox.cs
public class CheckComboBox : ListBox, IDataInput
{
....
}
and his style with the Template : CheckComboBoxBaseStyle.xaml
<ControlTemplate x:Key="CheckComboBoxTemplate" TargetType="hc:CheckComboBox">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" PlacementTarget="{Binding ElementName=toggleButton}" StaysOpen="False" IsOpen="{Binding IsDropDownOpen,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" AllowsTransparency="true" Grid.ColumnSpan="2" PopupAnimation="{StaticResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Decorator Margin="8 0">
<Border Effect="{StaticResource EffectShadow2}" BorderThickness="0,1,0,0" Margin="0,0,0,8" CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" x:Name="dropDownBorder" MaxHeight="{TemplateBinding MaxDropDownHeight}" BorderBrush="{DynamicResource BorderBrush}" Background="{DynamicResource RegionBrush}">
<hc:ToggleBlock IsChecked="{Binding HasItems,RelativeSource={RelativeSource TemplatedParent},Mode=OneWay}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch">
<hc:ToggleBlock.CheckedContent>
<Grid Margin="0,4" ClipToBounds="False">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<hc:CheckComboBoxItem x:Name="PART_SelectAll" Style="{TemplateBinding ItemContainerStyle}" IsEnabled="{TemplateBinding ShowSelectAllButton}" Visibility="{Binding ShowSelectAllButton,RelativeSource={RelativeSource TemplatedParent},Converter={StaticResource Boolean2VisibilityConverter}}" HorizontalContentAlignment="Stretch" Content="{ex:Lang Key={x:Static langs:LangKeys.All}}"/>
<ScrollViewer x:Name="DropDownScrollViewer" Grid.Row="1">
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Grid>
</hc:ToggleBlock.CheckedContent>
<hc:ToggleBlock.UnCheckedContent>
<hc:Empty/>
</hc:ToggleBlock.UnCheckedContent>
</hc:ToggleBlock>
</Border>
</Decorator>
</Popup>
<ToggleButton Grid.Column="0" x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" Style="{StaticResource CheckComboBoxToggleButton}"/>
<Border Grid.Column="0" Margin="-1,1">
<hc:UniformSpacingPanel x:Name="PART_Panel" Margin="{TemplateBinding Padding}" Spacing="4" ChildWrapping="Wrap" ItemVerticalAlignment="Center"/>
</Border>
</Grid>
</ControlTemplate>
π‘ The idea: It is good that Handy create style for all control and his new ones !! But the Template property should not be define in the Style ! I have not finish my test but : If new control was create like a UserControl the template will not be broken. It is better with an example :
CheckComboBox.xaml.cs (still the same except that I make it .xaml.cs as a usercontrol but actually it is just a convention)
public class CheckComboBox : ListBox, IDataInput
{
....
}
Here the Template and "Style" to imitate basic style of wpf
CheckComboBox.xaml
<ListBox x:Class="HandyControl.Controls.CheckComboBox"
...
Border="..."
<!-- default property-->
>
<ListBox.Template>
<ControlTemplate x:Key="CheckComboBoxTemplate" TargetType="hc:CheckComboBox">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" PlacementTarget="{Binding ElementName=toggleButton}" StaysOpen="False" IsOpen="{Binding IsDropDownOpen,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" AllowsTransparency="true" Grid.ColumnSpan="2" PopupAnimation="{StaticResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Decorator Margin="8 0">
<Border Effect="{StaticResource EffectShadow2}" BorderThickness="0,1,0,0" Margin="0,0,0,8" CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" x:Name="dropDownBorder" MaxHeight="{TemplateBinding MaxDropDownHeight}" BorderBrush="{DynamicResource BorderBrush}" Background="{DynamicResource RegionBrush}">
<hc:ToggleBlock IsChecked="{Binding HasItems,RelativeSource={RelativeSource TemplatedParent},Mode=OneWay}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch">
<hc:ToggleBlock.CheckedContent>
<Grid Margin="0,4" ClipToBounds="False">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<hc:CheckComboBoxItem x:Name="PART_SelectAll" Style="{TemplateBinding ItemContainerStyle}" IsEnabled="{TemplateBinding ShowSelectAllButton}" Visibility="{Binding ShowSelectAllButton,RelativeSource={RelativeSource TemplatedParent},Converter={StaticResource Boolean2VisibilityConverter}}" HorizontalContentAlignment="Stretch" Content="{ex:Lang Key={x:Static langs:LangKeys.All}}"/>
<ScrollViewer x:Name="DropDownScrollViewer" Grid.Row="1">
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Grid>
</hc:ToggleBlock.CheckedContent>
<hc:ToggleBlock.UnCheckedContent>
<hc:Empty/>
</hc:ToggleBlock.UnCheckedContent>
</hc:ToggleBlock>
</Border>
</Decorator>
</Popup>
<ToggleButton Grid.Column="0" x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" Style="{StaticResource CheckComboBoxToggleButton}"/>
<Border Grid.Column="0" Margin="-1,1">
<hc:UniformSpacingPanel x:Name="PART_Panel" Margin="{TemplateBinding Padding}" Spacing="4" ChildWrapping="Wrap" ItemVerticalAlignment="Center"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
</Trigger>
<Trigger Property="hc:DropDownElement.ConsistentWidth" Value="True">
<Setter Property="MaxWidth" TargetName="dropDownBorder" Value="{Binding ActualWidth, ElementName=toggleButton}"/>
<Setter Property="MinWidth" TargetName="dropDownBorder" Value="{Binding ActualWidth, ElementName=toggleButton}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true"/>
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="true" SourceName="toggleButton"/>
<Condition Property="IsOpen" Value="false" SourceName="PART_Popup"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="{DynamicResource SecondaryBorderBrush}"/>
</MultiTrigger>
<Trigger Property="IsOpen" Value="True" SourceName="PART_Popup">
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryBrush}"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" Value="0.4" TargetName="toggleButton"/>
<Setter Property="Opacity" Value="0.4" TargetName="PART_Panel"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ListBox.Template>
</ListBox>
CheckComboxBoxStyle.xaml
<!-- style of handy -->
<Style TargetType="hc:CheckComboBox" BasedOn="{StaticResource CheckComboBoxBaseStyle}"/>
In fact there is no main change for the final user and his way to access the control, and control become true new wpf control. I need to finish my test but for now it works this way !
To be honest, if you understand the difference between Style
and DefaultStyleKey
, you will find the current implementation is incorrect. You really should follow the instruction of official documents and examples(this, this, this and this).
The style assigned by Style
has higher priority over whitch assigned by DefaultStyleKey
.
DefaultStyleKey
is for control developers to define the basic appearance and template of custom controls. Style
is for control users to unify the controls' appearance in their project. The control developers should not use Style
and the control users should not use DefaultStyleKey
, too.
The other control developers also use DefaultStyleKey
to provide default appearance: wpftoolkit-Calculator, ModernWpf-SplitButton, Fluent.Ribbon-Gallery, MahApps.Metro-FlipView. There are way more examples you can reference.