feat: add inner contents.

This commit is contained in:
rabbitism
2024-04-20 01:20:04 +08:00
parent 0dbe12fef1
commit f7a316340b
5 changed files with 257 additions and 125 deletions

View File

@@ -41,13 +41,22 @@
</u:TreeComboBox.ItemTemplate> </u:TreeComboBox.ItemTemplate>
</u:TreeComboBox> </u:TreeComboBox>
<ContentControl Content="{Binding SelectedItem}"> <u:TreeComboBox
<ContentControl.ContentTemplate> Classes="clearButton"
<DataTemplate DataType="vm:TreeComboBoxItemViewModel"> Width="300"
<TextBlock Text="{Binding ItemName}"></TextBlock> Watermark="Please select an item. "
</DataTemplate> HorizontalAlignment="Left"
</ContentControl.ContentTemplate> SelectedItem="{Binding SelectedItem}"
</ContentControl> InnerRightContent="Right"
InnerLeftContent="Left"
PopupInnerTopContent="Top"
PopupInnerBottomContent="Bottom"
ItemsSource="{Binding Items}">
<u:TreeComboBox.ItemTemplate>
<TreeDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding ItemName}" />
</TreeDataTemplate>
</u:TreeComboBox.ItemTemplate>
</u:TreeComboBox>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@@ -1,68 +1,92 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui" <ResourceDictionary
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="https://irihi.tech/ursa"
xmlns:iri="https://irihi.tech/shared"
xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls" xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls"
xmlns:converters1="clr-namespace:Ursa.Themes.Semi.Converters"> xmlns:converters1="clr-namespace:Ursa.Converters;assembly=Ursa"
xmlns:iri="https://irihi.tech/shared"
xmlns:u="https://irihi.tech/ursa">
<!-- Add Resources Here --> <!-- Add Resources Here -->
<converters:MarginMultiplierConverter x:Key="LeftMarginConverter" Indent="20" Left="True"/> <converters:MarginMultiplierConverter
<converters1:SelectedItemTemplateConverter x:Key="SelectedItemTemplateConverter"/> x:Key="LeftMarginConverter"
Indent="20"
Left="True" />
<ControlTheme x:Key="{x:Type u:TreeComboBox}" TargetType="u:TreeComboBox"> <ControlTheme x:Key="{x:Type u:TreeComboBox}" TargetType="u:TreeComboBox">
<Setter Property="Padding" Value="{DynamicResource ComboBoxSelectorDefaultPadding}"/> <Setter Property="Padding" Value="{DynamicResource ComboBoxSelectorDefaultPadding}" />
<Setter Property="FocusAdorner" Value="{x:Null}"/> <Setter Property="FocusAdorner" Value="{x:Null}" />
<Setter Property="Background" Value="{DynamicResource ComboBoxSelectorBackground}"/> <Setter Property="Background" Value="{DynamicResource ComboBoxSelectorBackground}" />
<Setter Property="CornerRadius" Value="{DynamicResource ComboBoxSelectorCornerRadius}"/> <Setter Property="CornerRadius" Value="{DynamicResource ComboBoxSelectorCornerRadius}" />
<Setter Property="VerticalContentAlignment" Value="Center"></Setter> <Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="BorderThickness" Value="1"/> <Setter Property="BorderThickness" Value="1" />
<Setter Property="Cursor" Value="Hand"/> <Setter Property="MinHeight" Value="{DynamicResource ComboBoxDefaultHeight}" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate TargetType="u:TreeComboBox"> <ControlTemplate TargetType="u:TreeComboBox">
<Grid ColumnDefinitions="*, Auto, Auto"> <Grid MinWidth="{TemplateBinding MinHeight}" ColumnDefinitions="Auto, *, Auto, Auto, Auto">
<Border Grid.Column="0" <Border
Name="Background" Name="Background"
Grid.ColumnSpan="3" Grid.Column="0"
Grid.ColumnSpan="5"
Background="{TemplateBinding Background}" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}" CornerRadius="{TemplateBinding CornerRadius}" />
MinHeight="32" <ContentPresenter
/> Grid.Column="0"
Margin="8,0,0,0"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Content="{TemplateBinding InnerLeftContent}"
Foreground="{DynamicResource TextBoxInnerForeground}"
IsVisible="{TemplateBinding InnerLeftContent,
Converter={x:Static ObjectConverters.IsNotNull}}" />
<TextBlock <TextBlock
Name="PlaceholderTextBlock" Name="PlaceholderTextBlock"
Grid.Column="0" Grid.Column="1"
Margin="{TemplateBinding Padding}" Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
TextTrimming="CharacterEllipsis"
Foreground="{TemplateBinding Foreground}" Foreground="{TemplateBinding Foreground}"
IsVisible="{TemplateBinding SelectionBoxItem,
Converter={x:Static ObjectConverters.IsNull}}"
Opacity="0.3" Opacity="0.3"
IsVisible="{TemplateBinding SelectionBoxItem, Converter={x:Static ObjectConverters.IsNull}}"
Text="{TemplateBinding Watermark}" Text="{TemplateBinding Watermark}"
></TextBlock> TextTrimming="CharacterEllipsis" />
<ContentPresenter <ContentPresenter
Grid.Column="0" Grid.Column="1"
Margin="{TemplateBinding Padding}" Margin="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
VerticalAlignment="Center" VerticalAlignment="Center"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding SelectionBoxItem}"> Content="{TemplateBinding SelectionBoxItem}">
<ContentPresenter.ContentTemplate> <ContentPresenter.ContentTemplate>
<MultiBinding Converter="{x:Static converters1:SelectedItemTemplateConverter.Instance}"> <MultiBinding Converter="{x:Static converters1:SelectionBoxTemplateConverter.Instance}">
<TemplateBinding Property="SelectedItemTemplate"/> <TemplateBinding Property="SelectedItemTemplate" />
<TemplateBinding Property="ItemTemplate"/> <TemplateBinding Property="ItemTemplate" />
</MultiBinding> </MultiBinding>
</ContentPresenter.ContentTemplate> </ContentPresenter.ContentTemplate>
</ContentPresenter> </ContentPresenter>
<Border <Button
x:Name="DropDownOverlay" Name="PART_ClearButton"
Grid.Column="2" Grid.Column="2"
Width="30" Command="{Binding $parent[iri:IClearControl].Clear}"
Margin="0,1,1,1" IsVisible="False"
HorizontalAlignment="Right" Theme="{DynamicResource InnerIconButton}"
Content="{DynamicResource IconButtonClearData}" />
<ContentPresenter
Grid.Column="3"
Margin="0 0 8 0"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Content="{TemplateBinding InnerRightContent}"
Foreground="{DynamicResource TextBoxInnerForeground}"
IsVisible="{TemplateBinding InnerRightContent,
Converter={x:Static ObjectConverters.IsNotNull}}" />
<Panel
Grid.Column="4"
Width="32"
Background="Transparent" Background="Transparent"
IsVisible="False" /> IsHitTestVisible="True">
<Panel Grid.Column="2" Width="32" IsHitTestVisible="False">
<PathIcon <PathIcon
x:Name="DropDownGlyph" x:Name="DropDownGlyph"
Width="12" Width="12"
@@ -81,34 +105,112 @@
MinHeight="{TemplateBinding MaxDropDownHeight}" MinHeight="{TemplateBinding MaxDropDownHeight}"
ClipToBounds="False" ClipToBounds="False"
InheritsTransform="True" InheritsTransform="True"
PlacementTarget="Background"
WindowManagerAddShadowHint="False"
IsLightDismissEnabled="True" IsLightDismissEnabled="True"
IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"> IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
PlacementTarget="Background"
WindowManagerAddShadowHint="False">
<Border <Border
Name="PopupBorder" Name="PopupBorder"
Margin="0 4" Margin="0,4"
Background="{DynamicResource ComboBoxPopupBackground}" Background="{DynamicResource ComboBoxPopupBackground}"
BorderBrush="{DynamicResource ComboBoxPopupBorderBrush}" BorderBrush="{DynamicResource ComboBoxPopupBorderBrush}"
BoxShadow="{DynamicResource ComboBoxPopupBoxShadow}" BoxShadow="{DynamicResource ComboBoxPopupBoxShadow}"
ClipToBounds="True" ClipToBounds="True"
CornerRadius="6" CornerRadius="6">
> <DockPanel LastChildFill="True">
<ContentPresenter
Name="PART_PopupHeader"
Margin="8,8 8 0"
Content="{TemplateBinding PopupInnerTopContent}"
DockPanel.Dock="Top" />
<ContentPresenter
Name="PART_PopupFooter"
Margin="8 0 8 8"
Content="{TemplateBinding PopupInnerBottomContent}"
DockPanel.Dock="Bottom" />
<ScrollViewer> <ScrollViewer>
<ItemsPresenter ItemsPanel="{TemplateBinding ItemsPanel}"/> <ItemsPresenter ItemsPanel="{TemplateBinding ItemsPanel}" />
</ScrollViewer> </ScrollViewer>
</DockPanel>
</Border> </Border>
</Popup> </Popup>
</Grid> </Grid>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
<Style Selector="^.clearButton, ^.ClearButton">
<Style Selector="^:pointerover /template/ Button#PART_ClearButton">
<Setter Property="IsVisible" Value="{Binding $parent[u:TreeComboBox].SelectionBoxItem, Converter={x:Static ObjectConverters.IsNotNull}}"/>
</Style>
</Style>
<Style Selector="^.Large">
<Setter Property="MinHeight" Value="{DynamicResource ComboBoxLargeHeight}"/>
</Style>
<Style Selector="^.Small">
<Setter Property="MinHeight" Value="{DynamicResource ComboBoxSmallHeight}"/>
</Style>
<!-- Pointerover State -->
<Style Selector="^:pointerover">
<Setter Property="Background" Value="{DynamicResource ComboBoxSelectorPointeroverBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxSelectorPointeroverBorderBrush}" />
</Style>
<Style Selector="^:pointerover /template/ PathIcon#DropDownGlyph">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxIconPointeroverForeground}" />
</Style>
<!-- Pressed State -->
<Style Selector="^:pressed">
<Setter Property="Background" Value="{DynamicResource ComboBoxSelectorPressedBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxSelectorPressedBorderBrush}" />
<Style Selector="^ /template/ PathIcon#DropDownGlyph">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxIconPressedForeground}" />
</Style>
</Style>
<Style Selector="^:dropdownopen">
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxSelectorPressedBorderBrush}" />
</Style>
<!-- Disabled State -->
<Style Selector="^:disabled">
<Setter Property="Background" Value="{DynamicResource ComboBoxSelectorDisabledBackground}" />
<Style Selector="^ /template/ ContentControl#ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxDisabledForeground}" />
</Style>
<Style Selector="^ /template/ TextBlock#PlaceholderTextBlock">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxDisabledForeground}" />
</Style>
<Style Selector="^ /template/ PathIcon#DropDownGlyph">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxIconDisabledForeground}" />
</Style>
</Style>
<!-- Error State -->
<Style Selector="^:error">
<Style Selector="^ /template/ Border#Background">
<Setter Property="Background" Value="{DynamicResource DataValidationErrorsBackground}" />
</Style>
<Style Selector="^:pointerover /template/ Border#Background">
<Setter Property="Background" Value="{DynamicResource DataValidationErrorsPointerOverBackground}" />
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
<Style Selector="^:pressed /template/ Border#Background">
<Setter Property="Background" Value="{DynamicResource DataValidationErrorsPressedBackground}" />
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
<Style Selector="^:focus /template/ Border#Background">
<Setter Property="Background" Value="{DynamicResource DataValidationErrorsSelectedBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource DataValidationErrorsSelectedBorderBrush}" />
</Style>
</Style>
</ControlTheme> </ControlTheme>
<ControlTheme x:Key="{x:Type u:TreeComboBoxItem}" TargetType="u:TreeComboBoxItem"> <ControlTheme x:Key="{x:Type u:TreeComboBoxItem}" TargetType="u:TreeComboBoxItem">
<Setter Property="Background" Value="{DynamicResource TreeViewItemDefaultBackground}"/> <Setter Property="Background" Value="{DynamicResource TreeViewItemDefaultBackground}" />
<Setter Property="Foreground" Value="{DynamicResource TreeViewItemDefaultForeground}"/> <Setter Property="Foreground" Value="{DynamicResource TreeViewItemDefaultForeground}" />
<Setter Property="CornerRadius" Value="3"/> <Setter Property="CornerRadius" Value="3" />
<Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate TargetType="u:TreeComboBoxItem"> <ControlTemplate TargetType="u:TreeComboBoxItem">
<StackPanel> <StackPanel>
@@ -122,29 +224,35 @@
TemplatedControl.IsTemplateFocusTarget="True"> TemplatedControl.IsTemplateFocusTarget="True">
<Grid <Grid
Name="{x:Static iri:PartNames.PART_Header}" Name="{x:Static iri:PartNames.PART_Header}"
Margin="{TemplateBinding Level, Mode=OneWay, Converter={StaticResource LeftMarginConverter}}" Margin="{TemplateBinding Level,
Mode=OneWay,
Converter={StaticResource LeftMarginConverter}}"
ColumnDefinitions="Auto, *"> ColumnDefinitions="Auto, *">
<ToggleButton <ToggleButton
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}"
Theme="{DynamicResource ToggleButtonTreeViewItemIconButton}"
Name="PART_ExpandCollapseChevron" Name="PART_ExpandCollapseChevron"
Grid.Column="0" Grid.Column="0"
Padding="{DynamicResource TreeViewItemIconMargin}" Padding="{DynamicResource TreeViewItemIconMargin}"
Focusable="False"></ToggleButton> Focusable="False"
IsChecked="{TemplateBinding IsExpanded,
Mode=TwoWay}"
Theme="{DynamicResource ToggleButtonTreeViewItemIconButton}" />
<ContentPresenter <ContentPresenter
Grid.Column="1"
Name="{x:Static iri:PartNames.PART_HeaderPresenter}" Name="{x:Static iri:PartNames.PART_HeaderPresenter}"
Grid.Column="1"
Margin="{TemplateBinding Padding}" Margin="{TemplateBinding Padding}"
Padding="{DynamicResource TreeViewItemPadding}" Padding="{DynamicResource TreeViewItemPadding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}"
Foreground="{TemplateBinding Foreground}"
Content="{TemplateBinding Header}" Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}" ContentTemplate="{TemplateBinding HeaderTemplate}"
Focusable="False"/> Focusable="False"
Foreground="{TemplateBinding Foreground}" />
</Grid> </Grid>
</Border> </Border>
<ItemsPresenter IsVisible="{TemplateBinding IsExpanded}" Name="{x:Static iri:PartNames.PART_ItemsPresenter}" ItemsPanel="{TemplateBinding ItemsPanel}"/> <ItemsPresenter
Name="{x:Static iri:PartNames.PART_ItemsPresenter}"
IsVisible="{TemplateBinding IsExpanded}"
ItemsPanel="{TemplateBinding ItemsPanel}" />
</StackPanel> </StackPanel>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>

View File

@@ -1,24 +0,0 @@
using System.Globalization;
using Avalonia;
using Avalonia.Controls.Templates;
using Avalonia.Data.Converters;
namespace Ursa.Themes.Semi.Converters;
public class SelectedItemTemplateConverter: IMultiValueConverter
{
public static SelectedItemTemplateConverter Instance { get; } = new SelectedItemTemplateConverter();
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
{
if(values.Count>0 && values[0] is IDataTemplate template1)
{
return template1;
}
if(values.Count>1 && values[1] is IDataTemplate template2)
{
return template2;
}
return AvaloniaProperty.UnsetValue;
}
}

View File

@@ -12,14 +12,18 @@ using Avalonia.Metadata;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Irihi.Avalonia.Shared.Common; using Irihi.Avalonia.Shared.Common;
using Irihi.Avalonia.Shared.Contracts; using Irihi.Avalonia.Shared.Contracts;
using Irihi.Avalonia.Shared.Helpers;
using Size = Avalonia.Size; using Size = Avalonia.Size;
namespace Ursa.Controls; namespace Ursa.Controls;
[TemplatePart(PartNames.PART_Popup, typeof(Popup))] [TemplatePart(PartNames.PART_Popup, typeof(Popup))]
[PseudoClasses(PC_DropdownOpen)]
public class TreeComboBox: ItemsControl, IClearControl, IInnerContentControl, IPopupInnerContent public class TreeComboBox: ItemsControl, IClearControl, IInnerContentControl, IPopupInnerContent
{ {
public const string PC_DropdownOpen = ":dropdownopen";
private Popup? _popup; private Popup? _popup;
private static readonly FuncTemplate<Panel?> DefaultPanel = private static readonly FuncTemplate<Panel?> DefaultPanel =
@@ -102,11 +106,48 @@ public class TreeComboBox: ItemsControl, IClearControl, IInnerContentControl, IP
set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value); set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value);
} }
public static readonly StyledProperty<object?> InnerLeftContentProperty = AvaloniaProperty.Register<TreeComboBox, object?>(
nameof(InnerLeftContent));
public object? InnerLeftContent
{
get => GetValue(InnerLeftContentProperty);
set => SetValue(InnerLeftContentProperty, value);
}
public static readonly StyledProperty<object?> InnerRightContentProperty = AvaloniaProperty.Register<TreeComboBox, object?>(
nameof(InnerRightContent));
public object? InnerRightContent
{
get => GetValue(InnerRightContentProperty);
set => SetValue(InnerRightContentProperty, value);
}
public static readonly StyledProperty<object?> PopupInnerTopContentProperty = AvaloniaProperty.Register<TreeComboBox, object?>(
nameof(PopupInnerTopContent));
public object? PopupInnerTopContent
{
get => GetValue(PopupInnerTopContentProperty);
set => SetValue(PopupInnerTopContentProperty, value);
}
public static readonly StyledProperty<object?> PopupInnerBottomContentProperty = AvaloniaProperty.Register<TreeComboBox, object?>(
nameof(PopupInnerBottomContent));
public object? PopupInnerBottomContent
{
get => GetValue(PopupInnerBottomContentProperty);
set => SetValue(PopupInnerBottomContentProperty, value);
}
static TreeComboBox() static TreeComboBox()
{ {
ItemsPanelProperty.OverrideDefaultValue<TreeComboBox>(DefaultPanel); ItemsPanelProperty.OverrideDefaultValue<TreeComboBox>(DefaultPanel);
FocusableProperty.OverrideDefaultValue<TreeComboBox>(true); FocusableProperty.OverrideDefaultValue<TreeComboBox>(true);
SelectedItemProperty.Changed.AddClassHandler<TreeComboBox, object?>((box, args) => box.OnSelectedItemChanged(args)); SelectedItemProperty.Changed.AddClassHandler<TreeComboBox, object?>((box, args) => box.OnSelectedItemChanged(args));
IsDropDownOpenProperty.AffectsPseudoClass<TreeComboBox>(PC_DropdownOpen);
} }
private void OnSelectedItemChanged(AvaloniaPropertyChangedEventArgs<object?> args) private void OnSelectedItemChanged(AvaloniaPropertyChangedEventArgs<object?> args)
@@ -293,9 +334,4 @@ public class TreeComboBox: ItemsControl, IClearControl, IInnerContentControl, IP
{ {
SelectedItem = null; SelectedItem = null;
} }
public object? InnerLeftContent { get; set; }
public object? InnerRightContent { get; set; }
public object? PopupInnerTopContent { get; set; }
public object? PopupInnerBottomContent { get; set; }
} }

View File

@@ -6,11 +6,14 @@ namespace Ursa.Converters;
public class SelectionBoxTemplateConverter: IMultiValueConverter public class SelectionBoxTemplateConverter: IMultiValueConverter
{ {
public static SelectionBoxTemplateConverter Instance { get; } = new();
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture) public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
{ {
var selectedItemTemplate = values.Count > 0 ? values[0] as IDataTemplate : null; for (int i = 0; i < values.Count; i++)
if (selectedItemTemplate is not null) return selectedItemTemplate; {
var itemTemplate = values.Count > 1 ? values[1] as IDataTemplate : null; if (values[i] is IDataTemplate template) return template;
return itemTemplate; }
return null;
} }
} }