如何使用MVVM与WPF Canvas交互?

byqmnocz  于 2023-10-22  发布在  其他
关注(0)|答案(2)|浏览(150)

MVVM的几乎所有示例都是具有视图的应用程序,该视图可以由列表框、文本框、标签、按钮等组成。来呈现和处理数据。ListBox、TextBox和Label绑定到ViewModel中的属性,而Buttons绑定到Commands。这些类型的视图本质上主要是静态的。
然而,没有任何真实的例子来说明如何处理流体视图,例如绘图程序。当你想到它的时候,绘图程序只是一个画布。因为既没有要绑定的按钮,也没有要交互的列表框、文本框或标签,那么您应该如何与Canvas交互?
到目前为止,我发现与Canvas交互的唯一方法是使用Canvas的代码隐藏来使用鼠标事件,以允许您通过公开的方法将数据传递到ViewModel中,如Add(Point point point),该方法将添加一个点到ObservableCollection中,仅绑定到ItemsControl,后者将基于ItemTemplate处理点数据。

CanvasView

<ItemsControl x:Name="itemsControl" ItemsSource="{Binding CanvasObjectModels}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas Width="1000" Height="1000" Background="GhostWhite"
                MouseDown="Canvas_MouseDown"/>-->
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding Origin.X}" />
            <Setter Property="Canvas.Top" Value="{Binding Origin.Y}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <CanvasObject:Node Origin="{Binding Origin}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

IMyCanvas

public interface IMyCanvas
{
    public void AddCanvasObject(Point origin);
}

CanvasView Code-Behind

public partial class CanvasView : UserControl
{
    private IMyCanvas pViewModel;

    public CanvasView()
    {
        InitializeComponent();
        Loaded += InitializeViewModel;
    }

    private void InitializeViewModel(object sender, RoutedEventArgs e)
    {
        if (DataContext is IMyCanvas viewModel)
            pViewModel = viewModel;
        else
            throw new Exception("DataContext does not implement IMyCanvas!");
            
        Loaded -= InitializeViewModel;
    }

    private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
    {
        var canvas = sender as Canvas;

        if (e.LeftButton == MouseButtonState.Pressed)
        {
            pViewModel.AddCanvasObject(e.GetPosition(canvas));
        }
    }
}

画布对象模型

public class CanvasObjectModel
{
    public Point Origin { get; set; }
}

画布视图模型

internal class CanvasViewModel : ViewModelBase, IMyCanvas
{
    public ObservableCollection<CanvasObjectModel> CanvasObjectModels { get; set; }

    public CanvasViewModel()
    {
        CanvasObjectModels = new ObservableCollection<CanvasObjectModel>();
    }

    public void AddCanvasObject(Point origin)
    {
        CanvasObjectModels.Add(new CanvasObjectModel() { Origin = origin});
    }
}

有没有更好的方法来做到这一点?

h7appiyu

h7appiyu1#

如果要将事件绑定到VM命令,可以使用Interactivity:

<Canvas Width="1000" Height="1000" Background="GhostWhite">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="MouseDown">
      <i:InvokeCommandAction Command="{Binding MyCommand}"/>
    </i:EventTrigger>
  </i:Interaction.Triggers>
<Canvas/>

有关更多信息,请查看the question

ogsagwnx

ogsagwnx2#

您可以使用Microsoft.Xaml.Behaviors.Wpf(NuGet Package)中的CallMethodAction行为,直接从XAML调用视图模型方法。
我给你一个按钮的例子,但是你可以用同样的方式处理画布上的事件

1.将此命名空间添加到您的XAML中:

xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

2.将 CallMethodAction 触发器添加到XAML中的按钮中:

<Button x:Name="button">
  <Behaviors:Interaction.Triggers>
    <Behaviors:EventTrigger EventName="Click" SourceObject="{Binding ElementName=button}">
      <Behaviors:CallMethodAction TargetObject="{Binding}" MethodName="IncrementCount"/>
    </Behaviors:EventTrigger>
  </Behaviors:Interaction.Triggers>
</Button>

3.在视图模型中放入IncrementCount方法:

public int Count { get; set; }

public void IncrementCount()
{
    Count++;
}

现在您可以跳过后面代码中的样板代码。此外,您可以在这里查看XamlBehaviorsWpf wiki:https://github.com/Microsoft/XamlBehaviorsWpf/wiki还有其他一些行为:)

相关问题