我正在尝试创建一个编辑器,您可以在其中将预定义的形状放置到画布上,然后与它们交互。我了解如何使用
Canvas.SetTop()
方法将形状放在画布上的特定位置。现在我不知道如何在渲染后更改形状的位置。我正在尝试遵守 MVVM 规则。到目前为止我已经创建了这个:
MainWindow(菜单并不重要,但发布以防万一):
<DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="Exit" />
</MenuItem>
<MenuItem Header="Action">
<MenuItem Header="Draw place" />
<MenuItem Header="Draw transition" />
<MenuItem Header="Draw arc" />
<MenuItem Header="Play" />
</MenuItem>
</Menu>
<Canvas Name="MyCanvas" PointerReleased="Canvas_OnPointerReleased" Background="#141414" Margin="10" />
</DockPanel>
MainWindow 背后代码:
private void Canvas_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
var viewModel = DataContext as MainWindowViewModel;
var clickPosition = e.GetPosition(MyCanvas);
viewModel?.CreateTransitionCommand.Execute(clickPosition);
}
主窗口视图模型:
public partial class MainWindowViewModel : ObservableObject
{
private ObservableCollection<NodeViewModelBase> Elements { get; } = new ObservableCollection<NodeViewModelBase>();
[RelayCommand]
private void CreateTransition(Point position)
{
var model = new Transition();
var viewModel = new TransitionViewModel(model) {X = position.X, Y = position.Y};
Elements.Add(viewModel);
}
}
Model Transition
我想这里并不重要,但我会展示TransitionViewModel
以及TransitionView
。
TransitionViewModel:
public partial class TransitionViewModel : NodeViewModelBase
{
public TransitionViewModel(Transition transition)
{
Transition = transition;
}
private Transition Transition { get; set; }
[ObservableProperty]
private double _x;
[ObservableProperty]
private double _y;
[ObservableProperty]
private string _borderBrush = "#444444";
[ObservableProperty]
private int _size = 40;
[ObservableProperty]
private string _name = "Transition";
[ObservableProperty]
private int _fontSize = 10;
}
过渡视图:
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:Editor.ViewModels"
mc:Ignorable="d"
x:Class="Editor.Views.TransitionView"
x:DataType="vm:TransitionViewModel">
<StackPanel>
<Border BorderBrush="{Binding BorderBrush}" BorderThickness="1" HorizontalAlignment="Center">
<Rectangle Fill="#141414" Height="{Binding Size}" Width="{Binding Size}" />
</Border>
<TextBlock Text="{Binding Name}" FontSize="{Binding FontSize}" HorizontalAlignment="Center" Margin="0, 3, 0, 0" />
</StackPanel>
</UserControl>
现在的问题是,如何高效、简单地将这个TransitionView添加到Canvas中。当然,我尝试跳过在
MainWindow code behind
中执行的命令,而是仅创建一个视图,设置其数据上下文,然后将其添加到画布。但我没有找到任何方法,如何在离开事件处理程序方法后更改其在画布上的位置。
所以我尝试寻找解决方案。起初,我尝试更改命令,因此它所做的只是向集合中添加一个元素。其他一切都由隐藏代码处理,以使其更加简单。我尝试设置属性并将它们绑定在我的
TransitionView
中,但它不起作用:
<UserControl.Styles>
<Style Selector="UserControl">
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
</Style>
</UserControl.Styles>
后来,我在互联网上发现,对于某些人来说 ItemsControl 有效。我尝试了多种方法。我得到的只是在屏幕上写的“Avalonia.Markup.Xaml.Templates.ItemsPanelTemplate”。这肯定表明它的实施是错误的,但我无法修复它。正如我所说,我尝试了多种方法,这是我的最后一种:
<Window.DataTemplates>
<DataTemplate DataType="vm:TransitionViewModel">
<views:TransitionView />
</DataTemplate>
</Window.DataTemplates>
<Canvas Name="MyCanvas" PointerReleased="Canvas_OnPointerReleased" Background="#141414" Margin="10">
<ItemsControl ItemsSource="{Binding Elements}">
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
我尝试使用
ContentControl
的原因是我不需要在画布上只绘制矩形(TransitionView),还需要绘制圆形。所以我尝试创建一个系统,它可以识别 ItemsSource 中的每个元素应该如何在画布上绘制。
我认为值得一提的是,我确实能够找到某种解决方案,但它并不理想,而且也无法发挥应有的作用。我尝试在
PointerRelease
中添加 TransitionView
监听器:
<StackPanel PointerReleased="InputElement_OnPointerReleased">
<Border BorderBrush="{Binding BorderBrush}" BorderThickness="1" HorizontalAlignment="Center">
<Rectangle Fill="#141414" Height="{Binding Size}" Width="{Binding Size}" />
</Border>
<TextBlock Text="{Binding Name}" FontSize="{Binding FontSize}" HorizontalAlignment="Center" Margin="0, 3, 0, 0" />
</StackPanel>
之后,在后面的代码中,我只是使用
Canvas.setTop(this, 100)
和 Canvas.setLeft(this, 100)
来确定它是否有效。令我惊讶的是,它成功了,并将画布上的矩形移动到了点 100,100。但是我的鼠标点击不仅被 TransitionView 中的 StackPanel 识别,而且还被 canvas 本身识别。所以我移动了我的视图,但也在原来的同一位置创建了一个新视图。我无法找到解决此问题的正确解决方案,但这可能是一种方法。
我的方法可能完全错误,我愿意接受任何建议。我对 Avalonia 很陌生,基本上对任何使用 MVVM 原则且其视图是用 XAML 编写的 UI 框架都很陌生。我正在寻找最优雅、最清晰、对委托人友好的解决方案。