feat: add mvvm demo.

This commit is contained in:
Dong Bin
2025-07-02 22:31:16 +08:00
parent 881b9ca7d3
commit 6deccdc1ac
4 changed files with 220 additions and 82 deletions

View File

@@ -5,65 +5,169 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:u="https://irihi.tech/ursa" xmlns:u="https://irihi.tech/ursa"
xmlns:viewModels="clr-namespace:Ursa.Demo.ViewModels"
d:DesignHeight="450" d:DesignHeight="450"
d:DesignWidth="800" d:DesignWidth="800"
x:DataType="viewModels:AnchorDemoViewModel"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid ColumnDefinitions="*, Auto"> <TabControl>
<ScrollViewer <TabItem Header="XAML Inline">
Name="container" <Grid ColumnDefinitions="*, Auto">
Grid.Column="0" <ScrollViewer
VerticalAlignment="Stretch"> Name="container1"
<StackPanel> Grid.Column="0"
<Rectangle VerticalAlignment="Stretch">
Name="rectangle1" <StackPanel>
Height="300" <StackPanel.Styles>
HorizontalAlignment="Stretch" <Style Selector="TextBlock">
Fill="{DynamicResource SemiRed2}" /> <Setter Property="HorizontalAlignment" Value="Center" />
<Rectangle <Setter Property="VerticalAlignment" Value="Center" />
Name="rectangle2" </Style>
Height="300" </StackPanel.Styles>
HorizontalAlignment="Stretch" <Border
Fill="{DynamicResource SemiPink1}" /> Name="a1"
<Rectangle Height="300"
Name="rectangle3" HorizontalAlignment="Stretch"
Height="300" Background="{DynamicResource SemiRed2}">
HorizontalAlignment="Stretch" <TextBlock Text="Border 1" />
Fill="{DynamicResource SemiPurple1}" /> </Border>
<Rectangle <Border
Name="rectangle4" Name="a2"
Height="300" Height="300"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Fill="{DynamicResource SemiViolet1}" /> Background="{DynamicResource SemiPink1}">
<Rectangle <TextBlock Text="Border 2" />
Name="rectangle5" </Border>
Height="300" <Border
HorizontalAlignment="Stretch" Name="a3"
Fill="{DynamicResource SemiIndigo1}" /> Height="300"
<Rectangle HorizontalAlignment="Stretch"
Name="rectangle6" Background="{DynamicResource SemiPurple1}">
Height="300" <TextBlock Text="Border 3" />
HorizontalAlignment="Stretch" </Border>
Fill="{DynamicResource SemiBlue1}" /> <Border
<Rectangle Name="a4"
Name="rectangle7" Height="300"
Height="300" HorizontalAlignment="Stretch"
HorizontalAlignment="Stretch" Background="{DynamicResource SemiViolet1}">
Fill="{DynamicResource SemiLightBlue1}" /> <TextBlock Text="Border 4" />
</StackPanel> </Border>
</ScrollViewer> <Border
<u:Anchor Name="a5"
Grid.Column="1" Height="300"
Width="200" HorizontalAlignment="Stretch"
Margin="24" Background="{DynamicResource SemiIndigo1}">
TargetContainer="{Binding ElementName=container}"> <TextBlock Text="Border 5" />
<u:AnchorItem Content="Rectangle 1" Target="{Binding #rectangle1}" /> </Border>
<u:AnchorItem Content="Rectangle 2" Target="{Binding #rectangle2}" /> <Border
<u:AnchorItem Content="Rectangle 3" Target="{Binding #rectangle3}" /> Name="a6"
<u:AnchorItem Content="Rectangle 4" Target="{Binding #rectangle4}" /> Height="300"
<u:AnchorItem Content="Rectangle 5" Target="{Binding #rectangle5}" /> HorizontalAlignment="Stretch"
<u:AnchorItem Content="Rectangle 6" Target="{Binding #rectangle6}" /> Background="{DynamicResource SemiBlue1}">
<u:AnchorItem Content="Rectangle 7" Target="{Binding #rectangle7}" /> <TextBlock Text="Border 6" />
</Border>
<Border
Name="a7"
Height="300"
HorizontalAlignment="Stretch"
Background="{DynamicResource SemiLightBlue1}">
<TextBlock Text="Border 7" />
</Border>
</StackPanel>
</ScrollViewer>
<u:Anchor
Grid.Column="1"
Width="200"
Margin="24"
TargetContainer="{Binding ElementName=container1}">
<u:AnchorItem Content="Rectangle 1" Target="{Binding #a1}" />
<u:AnchorItem Content="Rectangle 2" Target="{Binding #a2}" />
<u:AnchorItem Content="Rectangle 3" Target="{Binding #a3}" />
<u:AnchorItem Content="Rectangle 4" Target="{Binding #a4}" />
<u:AnchorItem Content="Rectangle 5" Target="{Binding #a5}" />
<u:AnchorItem Content="Rectangle 6" Target="{Binding #a6}" />
<u:AnchorItem Content="Rectangle 7" Target="{Binding #a7}" />
</u:Anchor> </u:Anchor>
</Grid> </Grid>
</UserControl> </TabItem>
<TabItem Header="MVVM">
<Grid ColumnDefinitions="*, Auto">
<ScrollViewer
Name="container2"
Grid.Column="0"
VerticalAlignment="Stretch">
<StackPanel>
<StackPanel.Styles>
<Style Selector="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</StackPanel.Styles>
<Border
Height="300"
HorizontalAlignment="Stretch"
u:Anchor.AnchorId="anchor1"
Background="{DynamicResource SemiRed2}">
<TextBlock Text="Border 1" />
</Border>
<Border
Height="300"
HorizontalAlignment="Stretch"
u:Anchor.AnchorId="anchor2"
Background="{DynamicResource SemiPink1}">
<TextBlock Text="Border 2" />
</Border>
<Border
Height="300"
HorizontalAlignment="Stretch"
u:Anchor.AnchorId="anchor3"
Background="{DynamicResource SemiPurple1}">
<TextBlock Text="Border 3" />
</Border>
<Border
Height="300"
HorizontalAlignment="Stretch"
u:Anchor.AnchorId="anchor4"
Background="{DynamicResource SemiViolet1}">
<TextBlock Text="Border 4" />
</Border>
<Border
Height="300"
HorizontalAlignment="Stretch"
u:Anchor.AnchorId="anchor5"
Background="{DynamicResource SemiIndigo1}">
<TextBlock Text="Border 5" />
</Border>
<Border
Height="300"
HorizontalAlignment="Stretch"
u:Anchor.AnchorId="anchor6"
Background="{DynamicResource SemiBlue1}">
<TextBlock Text="Border 6" />
</Border>
<Border
Height="300"
HorizontalAlignment="Stretch"
u:Anchor.AnchorId="anchor7"
Background="{DynamicResource SemiLightBlue1}">
<TextBlock Text="Border 7" />
</Border>
</StackPanel>
</ScrollViewer>
<u:Anchor
Grid.Column="1"
MinWidth="300"
ItemsSource="{Binding AnchorItems}"
TargetContainer="{Binding #container2}">
<u:Anchor.Styles>
<Style x:DataType="viewModels:AnchorItemViewModel" Selector="u|AnchorItem">
<Setter Property="AnchorId" Value="{Binding AnchorId}" />
<Setter Property="Content" Value="{Binding Header}" />
</Style>
</u:Anchor.Styles>
</u:Anchor>
</Grid>
</TabItem>
</TabControl>
</UserControl>

View File

@@ -1,8 +1,24 @@
using System.Collections.Generic;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
namespace Ursa.Demo.ViewModels; namespace Ursa.Demo.ViewModels;
public partial class AnchorDemoViewModel: ObservableObject public partial class AnchorDemoViewModel: ObservableObject
{ {
public List<AnchorItemViewModel> AnchorItems { get; } = new()
{
new AnchorItemViewModel { AnchorId = "anchor1", Header = "Anchor 1" },
new AnchorItemViewModel { AnchorId = "anchor2", Header = "Anchor 2" },
new AnchorItemViewModel { AnchorId = "anchor3", Header = "Anchor 3" },
new AnchorItemViewModel { AnchorId = "anchor4", Header = "Anchor 4" },
new AnchorItemViewModel { AnchorId = "anchor5", Header = "Anchor 5" },
new AnchorItemViewModel { AnchorId = "anchor6", Header = "Anchor 6" },
new AnchorItemViewModel { AnchorId = "anchor7", Header = "Anchor 7" },
};
}
public partial class AnchorItemViewModel: ObservableObject
{
[ObservableProperty] private string? _anchorId;
[ObservableProperty] private string? _header;
} }

View File

@@ -3,7 +3,9 @@ using Avalonia.Animation;
using Avalonia.Animation.Easings; using Avalonia.Animation.Easings;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.VisualTree;
namespace Ursa.Controls; namespace Ursa.Controls;
@@ -18,11 +20,11 @@ public class Anchor: SelectingItemsControl
set => SetValue(TargetContainerProperty, value); set => SetValue(TargetContainerProperty, value);
} }
public static readonly AttachedProperty<string> AnchorIdProperty = public static readonly AttachedProperty<string?> AnchorIdProperty =
AvaloniaProperty.RegisterAttached<Anchor, Control, string>("AnchorId"); AvaloniaProperty.RegisterAttached<Anchor, Visual, string?>("AnchorId");
public static void SetAnchorId(Control obj, string value) => obj.SetValue(AnchorIdProperty, value); public static void SetAnchorId(Visual obj, string? value) => obj.SetValue(AnchorIdProperty, value);
public static string GetAnchorId(Control obj) => obj.GetValue(AnchorIdProperty); public static string? GetAnchorId(Visual obj) => obj.GetValue(AnchorIdProperty);
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey) protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
@@ -40,24 +42,16 @@ public class Anchor: SelectingItemsControl
{ {
if (TargetContainer is null) if (TargetContainer is null)
return; return;
var target = TargetContainer.GetVisualDescendants().FirstOrDefault(a=>Anchor.GetAnchorId(a) == anchorId);
var target = TargetContainer.FindControl<Control>(anchorId); if (target is null) return;
if (target is null) ScrollToAnchor(target);
return;
var targetPosition = target.TranslatePoint(new Point(0, 0), TargetContainer);
if (targetPosition.HasValue)
{
TargetContainer.Offset = new Vector(0, targetPosition.Value.Y);
}
} }
internal void ScrollToAnchor(Control target) internal void ScrollToAnchor(Visual target)
{ {
if (TargetContainer is null) if (TargetContainer is null)
return; return;
TargetContainer.Loaded += OnTargetLoaded;
var targetPosition = target.TranslatePoint(new Point(0, 0), TargetContainer); var targetPosition = target.TranslatePoint(new Point(0, 0), TargetContainer);
if (targetPosition.HasValue) if (targetPosition.HasValue)
{ {
@@ -95,5 +89,25 @@ public class Anchor: SelectingItemsControl
// TargetContainer.Offset = TargetContainer.Offset.WithY(TargetContainer.Offset.Y + targetPosition.Value.Y); // TargetContainer.Offset = TargetContainer.Offset.WithY(TargetContainer.Offset.Y + targetPosition.Value.Y);
} }
} }
private void OnTargetLoaded(object sender, RoutedEventArgs e)
{
if (sender is ScrollViewer scrollViewer)
{
scrollViewer.Loaded -= OnTargetLoaded;
if (scrollViewer.Content is Visual target)
{
var anchorId = GetAnchorId(target);
if (!string.IsNullOrEmpty(anchorId))
{
ScrollToAnchor(anchorId);
}
}
}
}
public void InvalidatePositions()
{
}
} }

View File

@@ -17,12 +17,12 @@ public class AnchorItem: ContentControl
set => SetValue(TargetProperty, value); set => SetValue(TargetProperty, value);
} }
public static readonly StyledProperty<string?> AnchorNameProperty = AvaloniaProperty.Register<AnchorItem, string?>( public static readonly StyledProperty<string?> AnchorIdProperty = AvaloniaProperty.Register<AnchorItem, string?>(
nameof(AnchorName)); nameof(AnchorId));
public string? AnchorName public string? AnchorId
{ {
get => GetValue(AnchorNameProperty); get => GetValue(AnchorIdProperty);
set => SetValue(AnchorNameProperty, value); set => SetValue(AnchorIdProperty, value);
} }
private Anchor? _root; private Anchor? _root;
@@ -43,5 +43,9 @@ public class AnchorItem: ContentControl
{ {
_root.ScrollToAnchor(Target); _root.ScrollToAnchor(Target);
} }
else if (!string.IsNullOrEmpty(AnchorId))
{
_root.ScrollToAnchor(AnchorId);
}
} }
} }