feat: Add Card control and card demo with album images.
No files matched your search
@@ -0,0 +1,10 @@
|
||||
namespace HandyControlDemo.Data;
|
||||
|
||||
public class CardModel
|
||||
{
|
||||
public string? Header { get; set; }
|
||||
|
||||
public string? Content { get; set; }
|
||||
|
||||
public string? Footer { get; set; }
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
<None Remove="Resources\Img\cloud.png" />
|
||||
<None Remove="Resources\Img\icon.ico" />
|
||||
<None Remove="Resources\Img\Avatar\*.png" />
|
||||
<None Remove="Resources\Img\Album\*.jpg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -27,6 +28,7 @@
|
||||
<AvaloniaResource Include="Resources\Img\cloud.png" />
|
||||
<AvaloniaResource Include="Resources\Img\icon.ico" />
|
||||
<AvaloniaResource Include="Resources\Img\Avatar\*.png" />
|
||||
<AvaloniaResource Include="Resources\Img\Album\*.jpg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -12,6 +13,31 @@ namespace HandyControlDemo.Service;
|
||||
|
||||
public class DataService
|
||||
{
|
||||
internal ObservableCollection<CardModel> GetCardDataList()
|
||||
{
|
||||
return
|
||||
[
|
||||
new CardModel { Header = "Atomic", Content = "avares://HandyControlDemo/Resources/Img/Album/1.jpg", Footer = "Stive Morgan" },
|
||||
new CardModel { Header = "Zinderlong", Content = "avares://HandyControlDemo/Resources/Img/Album/2.jpg", Footer = "Zonderling" },
|
||||
new CardModel { Header = "Busy Doin' Nothin'", Content = "avares://HandyControlDemo/Resources/Img/Album/3.jpg", Footer = "Ace Wilder" },
|
||||
new CardModel { Header = "Wrong", Content = "avares://HandyControlDemo/Resources/Img/Album/4.jpg", Footer = "Blaxy Girls" },
|
||||
new CardModel { Header = "The Lights", Content = "avares://HandyControlDemo/Resources/Img/Album/5.jpg", Footer = "Panda Eyes" },
|
||||
new CardModel { Header = "EA7-50-Cent Disco", Content = "avares://HandyControlDemo/Resources/Img/Album/6.jpg", Footer = "еяхат музыка" },
|
||||
new CardModel { Header = "Monsters", Content = "avares://HandyControlDemo/Resources/Img/Album/7.jpg", Footer = "Different Heaven" },
|
||||
new CardModel { Header = "Gangsta Walk", Content = "avares://HandyControlDemo/Resources/Img/Album/8.jpg", Footer = "Illusionize" },
|
||||
new CardModel { Header = "Won't Back Down", Content = "avares://HandyControlDemo/Resources/Img/Album/9.jpg", Footer = "Boehm / Benjamin Francis Leftwich" },
|
||||
new CardModel { Header = "Katchi", Content = "avares://HandyControlDemo/Resources/Img/Album/10.jpg", Footer = "Ofenbach / Nick Waterhouse" }
|
||||
];
|
||||
}
|
||||
|
||||
internal CardModel GetCardData()
|
||||
{
|
||||
return new CardModel
|
||||
{
|
||||
Content = $"avares://HandyControlDemo/Resources/Img/Album/{DateTime.Now.Second % 10 + 1}.jpg"
|
||||
};
|
||||
}
|
||||
|
||||
internal List<DemoDataModel> GetDemoDataList()
|
||||
{
|
||||
var list = new List<DemoDataModel>();
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform;
|
||||
|
||||
namespace HandyControlDemo.Tools.Converter;
|
||||
|
||||
public class StringToBitmapConverter : IValueConverter
|
||||
{
|
||||
public static readonly StringToBitmapConverter Instance = new();
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is not string path || string.IsNullOrEmpty(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var stream = AssetLoader.Open(new Uri(path));
|
||||
return new Bitmap(stream);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:vm="clr-namespace:HandyControlDemo.ViewModel"
|
||||
xmlns:data="clr-namespace:HandyControlDemo.Data"
|
||||
xmlns:cv="clr-namespace:HandyControlDemo.Tools.Converter"
|
||||
x:Class="HandyControlDemo.UserControl.CardDemo"
|
||||
x:DataType="vm:CardDemoViewModel"
|
||||
DataContext="{Binding CardDemo, Source={x:Static vm:ViewModelLocator.Instance}}">
|
||||
<UserControl.Resources>
|
||||
<cv:StringToBitmapConverter x:Key="StringToBitmap" />
|
||||
</UserControl.Resources>
|
||||
<ScrollViewer>
|
||||
<ItemsControl Margin="32"
|
||||
ItemsSource="{Binding DataList}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="data:CardModel">
|
||||
<hc:Card MaxWidth="240"
|
||||
BorderThickness="0"
|
||||
Effect="{StaticResource EffectShadow2}"
|
||||
Margin="8">
|
||||
<hc:Card.Footer>
|
||||
<StackPanel Margin="10">
|
||||
<TextBlock TextWrapping="NoWrap"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Theme="{StaticResource TextBlockLargeBold}"
|
||||
Text="{Binding Header}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock TextWrapping="NoWrap"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="{Binding Footer}"
|
||||
Margin="0,6,0,0"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
</hc:Card.Footer>
|
||||
<Border CornerRadius="4,4,0,0"
|
||||
ClipToBounds="True">
|
||||
<Image Width="240"
|
||||
Height="240"
|
||||
Source="{Binding Content, Converter={StaticResource StringToBitmap}}"
|
||||
Stretch="Uniform" />
|
||||
</Border>
|
||||
</hc:Card>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace HandyControlDemo.UserControl;
|
||||
|
||||
public partial class CardDemo : Avalonia.Controls.UserControl
|
||||
{
|
||||
public CardDemo()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,14 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:vm="clr-namespace:HandyControlDemo.ViewModel"
|
||||
xmlns:data="clr-namespace:HandyControlDemo.Data"
|
||||
xmlns:cv="clr-namespace:HandyControlDemo.Tools.Converter"
|
||||
x:Class="HandyControlDemo.UserControl.HoneycombPanelDemo"
|
||||
DataContext="{Binding HoneycombPanelDemo, Source={x:Static vm:ViewModelLocator.Instance}}">
|
||||
x:DataType="vm:CardDemoViewModel"
|
||||
DataContext="{Binding CardDemo, Source={x:Static vm:ViewModelLocator.Instance}}">
|
||||
<UserControl.Resources>
|
||||
<cv:StringToBitmapConverter x:Key="StringToBitmap" />
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="32">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
@@ -25,13 +31,14 @@
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DataTemplate DataType="data:CardModel">
|
||||
<Border Width="60"
|
||||
Height="60"
|
||||
Margin="6"
|
||||
CornerRadius="30"
|
||||
ClipToBounds="True">
|
||||
<Image Source="{Binding}" Stretch="UniformToFill" />
|
||||
<Image Source="{Binding Content, Converter={StaticResource StringToBitmap}}"
|
||||
Stretch="UniformToFill" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using HandyControlDemo.Data;
|
||||
using HandyControlDemo.Service;
|
||||
|
||||
namespace HandyControlDemo.ViewModel;
|
||||
|
||||
public partial class CardDemoViewModel : DemoViewModelBase<CardModel>
|
||||
{
|
||||
private readonly DataService _dataService;
|
||||
|
||||
public CardDemoViewModel(DataService dataService)
|
||||
{
|
||||
_dataService = dataService;
|
||||
DataList = dataService.GetCardDataList();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void AddItem() => DataList.Insert(0, _dataService.GetCardData());
|
||||
|
||||
[RelayCommand]
|
||||
private void RemoveItem()
|
||||
{
|
||||
if (DataList.Count > 0)
|
||||
{
|
||||
DataList.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace HandyControlDemo.ViewModel;
|
||||
|
||||
public partial class HoneycombPanelDemoViewModel : ObservableObject
|
||||
{
|
||||
private static readonly Random Random = new();
|
||||
|
||||
[ObservableProperty] private ObservableCollection<Bitmap> _dataList = [];
|
||||
|
||||
public HoneycombPanelDemoViewModel()
|
||||
{
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
DataList.Add(LoadRandomAvatar());
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void AddItem() => DataList.Insert(0, LoadRandomAvatar());
|
||||
|
||||
[RelayCommand]
|
||||
private void RemoveItem()
|
||||
{
|
||||
if (DataList.Count > 0)
|
||||
{
|
||||
DataList.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static Bitmap LoadRandomAvatar()
|
||||
{
|
||||
var uri = new Uri($"avares://HandyControlDemo/Resources/Img/Avatar/avatar{Random.Next(1, 7)}.png");
|
||||
using var stream = AssetLoader.Open(uri);
|
||||
return new Bitmap(stream);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ public class ViewModelLocator
|
||||
services.AddSingleton<DataService>();
|
||||
services.AddTransient<MainViewModel>();
|
||||
services.AddTransient<InputElementDemoViewModel>();
|
||||
services.AddTransient<HoneycombPanelDemoViewModel>();
|
||||
services.AddTransient<CardDemoViewModel>();
|
||||
|
||||
_serviceProvider = services.BuildServiceProvider();
|
||||
}
|
||||
@@ -29,5 +29,5 @@ public class ViewModelLocator
|
||||
|
||||
public InputElementDemoViewModel InputElementDemo => _serviceProvider.GetService<InputElementDemoViewModel>()!;
|
||||
|
||||
public HoneycombPanelDemoViewModel HoneycombPanelDemo => _serviceProvider.GetService<HoneycombPanelDemoViewModel>()!;
|
||||
public CardDemoViewModel CardDemo => _serviceProvider.GetService<CardDemoViewModel>()!;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Templates;
|
||||
|
||||
namespace HandyControl.Controls;
|
||||
|
||||
public class Card : ContentControl
|
||||
{
|
||||
public static readonly StyledProperty<object?> HeaderProperty =
|
||||
AvaloniaProperty.Register<Card, object?>(nameof(Header));
|
||||
|
||||
public object? Header
|
||||
{
|
||||
get => GetValue(HeaderProperty);
|
||||
set => SetValue(HeaderProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<IDataTemplate?> HeaderTemplateProperty =
|
||||
AvaloniaProperty.Register<Card, IDataTemplate?>(nameof(HeaderTemplate));
|
||||
|
||||
public IDataTemplate? HeaderTemplate
|
||||
{
|
||||
get => GetValue(HeaderTemplateProperty);
|
||||
set => SetValue(HeaderTemplateProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<string?> HeaderStringFormatProperty =
|
||||
AvaloniaProperty.Register<Card, string?>(nameof(HeaderStringFormat));
|
||||
|
||||
public string? HeaderStringFormat
|
||||
{
|
||||
get => GetValue(HeaderStringFormatProperty);
|
||||
set => SetValue(HeaderStringFormatProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<object?> FooterProperty =
|
||||
AvaloniaProperty.Register<Card, object?>(nameof(Footer));
|
||||
|
||||
public object? Footer
|
||||
{
|
||||
get => GetValue(FooterProperty);
|
||||
set => SetValue(FooterProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<IDataTemplate?> FooterTemplateProperty =
|
||||
AvaloniaProperty.Register<Card, IDataTemplate?>(nameof(FooterTemplate));
|
||||
|
||||
public IDataTemplate? FooterTemplate
|
||||
{
|
||||
get => GetValue(FooterTemplateProperty);
|
||||
set => SetValue(FooterTemplateProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<string?> FooterStringFormatProperty =
|
||||
AvaloniaProperty.Register<Card, string?>(nameof(FooterStringFormat));
|
||||
|
||||
public string? FooterStringFormat
|
||||
{
|
||||
get => GetValue(FooterStringFormatProperty);
|
||||
set => SetValue(FooterStringFormatProperty, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:ClassModifier="internal"
|
||||
xmlns:hc="clr-namespace:HandyControl.Controls">
|
||||
|
||||
<ControlTheme x:Key="CardBaseStyle"
|
||||
TargetType="hc:Card">
|
||||
<Setter Property="Background" Value="{DynamicResource RegionBrush}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}" />
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="CornerRadius" Value="{StaticResource DefaultCornerRadius}" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="hc:Card">
|
||||
<Border BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Border BorderThickness="0,0,0,1"
|
||||
IsVisible="{TemplateBinding Header, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}">
|
||||
<ContentPresenter Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}" />
|
||||
</Border>
|
||||
<ContentPresenter Grid.Row="1"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}" />
|
||||
<Border Grid.Row="2"
|
||||
BorderThickness="0,1,0,0"
|
||||
IsVisible="{TemplateBinding Footer, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}">
|
||||
<ContentPresenter Content="{TemplateBinding Footer}"
|
||||
ContentTemplate="{TemplateBinding FooterTemplate}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type hc:Card}"
|
||||
BasedOn="{StaticResource CardBaseStyle}"
|
||||
TargetType="hc:Card" />
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -57,6 +57,7 @@
|
||||
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/ContextMenu.axaml" />
|
||||
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/SelectableTextBlock.axaml" />
|
||||
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/HeaderedContentControl.axaml" />
|
||||
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/Card.axaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Styles.Resources>
|
||||
|
||||