如何将InkCanvas SelectedStrokes传递给viewmodel?

时间:2017-02-03 04:49:27

标签: wpf mvvm inkcanvas

在标准MVVM模式中,viewmodel如何识别InkCanvas中的选定笔划?

在具有InkCanvas知识的代码隐藏中,删除选定的笔划非常简单:

 private void btnDeleteSelectedStrokes_Click(object sender, RoutedEventArgs e)
        {
            StrokeCollection selectedStrokes = theInkCanvas.GetSelectedStrokes();
            theInkCanvas.Strokes.Remove(selectedStrokes);

        }

但这可以在MVVM中完成吗?

TIA

2 个答案:

答案 0 :(得分:1)

您可以使用行为来实现相同的功能。

public class InkCanvasDeleteBehavior : Behavior<Button>
{
    public InkCanvas Canvas
    {
        get { return (InkCanvas)GetValue(CanvasProperty); }
        set { SetValue(CanvasProperty, value); }
    }

    public static readonly DependencyProperty CanvasProperty =
        DependencyProperty.Register("Canvas", 
            typeof(InkCanvas), 
            typeof(InkCanvasDeleteBehavior), 
            new PropertyMetadata(null));



    protected override void OnAttached()
    {
        base.OnAttached();
        var btnDelete = this.AssociatedObject as Button;
        if(btnDelete!=null)
        {
            btnDelete.Click += BtnDelete_Click;
        }
    }

    private void BtnDelete_Click(object sender, RoutedEventArgs e)
    {
        if(this.Canvas!=null)
        {
            var stokeCollection = this.Canvas.InkPresenter.StrokeContainer.GetStrokes();
            foreach (var stroke in stokeCollection)
            {
                stroke.Selected = true;
            }
            this.Canvas.InkPresenter.StrokeContainer.DeleteSelected();
        }
    }
}

对于XAML,您可以这种方式使用该行为。

<Page
x:Class="Mock.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Mock"
xmlns:behavior="using:Mock.Behaviors"
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <InkCanvas x:Name="_canvas" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1"/>
    <Button Content="Delete" HorizontalAlignment="Center" Margin="5">
        <i:Interaction.Behaviors>
            <behavior:InkCanvasDeleteBehavior Canvas="{Binding ElementName=_canvas}"/>
        </i:Interaction.Behaviors>
    </Button>
</Grid>
</Page>
PS:我正在使用UWP。在WPF中,某些代码可能略有不同,但底层逻辑是相同的。

还有另一种方法可以做到这一点,方法是将对象通过命令参数传递给命令,然后使用按钮中的命令删除项目。那也行。如果您需要样品,请告诉我。

除此之外,您还可以使用行为将值传递给视图模型。您可以在视图模型中保留属性StrokeCollection,并传递对该行为的引用。当您在InkCanvas中绘制内容时,请更新行为中的StrokeCollection,该行为将反映在ViewModel中。

唯一的区别是,行为将附加到示例中的InkCanvas而不是Button。

答案 1 :(得分:0)

代替更好的解决方案,这对我有用:

XAML
xmlns:h="clr-namespace:Notepad.Helpers" 

<InkCanvas ... h:InkCanvasExtension.IsSelectionEnabled="True"
               h:InkCanvasExtension.TheSelectedStrokes="{Binding SelectedStrokes, Mode=TwoWay}"

附属物:

namespace Notepad.Helpers
{
    public static class InkCanvasExtension 
    {
        /*The provider class for an attached property (even if it is not registered as a dependency property) must provide static get and set accessors 
        * that follow the naming convention Set[AttachedPropertyName] and Get[AttachedPropertyName]. These accessors are required so that the acting XAML 
        * reader can recognize the property as an attribute in XAML and resolve the appropriate types.*/

        #region [IsSelectionEnabled]
        public static bool GetIsSelectionEnabled(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsSelectionEnabled);
        }

        public static void SetIsSelectionEnabled(DependencyObject obj, bool value)
        {
            obj.SetValue(IsSelectionEnabled, value);
        }

        // In XAML, IsSelectionEnabled = "true" will call OnIsSelectionEnabled().
        public static readonly DependencyProperty IsSelectionEnabled =
            DependencyProperty.RegisterAttached("IsSelectionEnabled",
            typeof(bool), typeof(InkCanvasExtension),
            new UIPropertyMetadata(false, OnIsSelectionEnabled));


        private static void OnIsSelectionEnabled(object sender, DependencyPropertyChangedEventArgs e)
        {
            InkCanvas ic = sender as InkCanvas;
            if (ic != null)
            {
                // get the value of IsSelectionEnabled (which is either true or false).
                bool isEnabled = (bool)e.NewValue;
                if (isEnabled)
                {
                    ic.SelectionChanged += OnSelectionChanged;
                }
                else
                {
                    ic.SelectionChanged -= OnSelectionChanged;
                }
            }
        }

        private static void OnSelectionChanged(object sender, EventArgs e)
        {
            // Assigning TheSelectedStrokes directly like: 
            //      TheSelectedStrokes = selectedStrokes
            // will not work and will break the binding. Must use:
            //      SetTheSelectedStrokes(ic, selectedStrokes);

            InkCanvas ic = sender as InkCanvas;
            StrokeCollection selectedStrokes = ic.GetSelectedStrokes();
            SetTheSelectedStrokes(ic, selectedStrokes);
        }


        #endregion

        #region [TheSelectedStrokes]
        public static StrokeCollection GetTheSelectedStrokes(DependencyObject obj)
        {
            return (StrokeCollection)obj.GetValue(TheSelectedStrokes);
        }

        public static void SetTheSelectedStrokes(DependencyObject obj, StrokeCollection value)
        {
            obj.SetValue(TheSelectedStrokes, value);
        }

        // by default binding works one way, i.e. loading changes from the view model, but not updating it back.
        // so must add FrameworkPropertyMetadataOptions.BindsTwoWayByDefault to send update to the viewmodel.
        public static readonly DependencyProperty TheSelectedStrokes =
                DependencyProperty.RegisterAttached("TheSelectedStrokes",
                typeof(StrokeCollection), typeof(InkCanvasExtension),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
        #endregion

    }

}

希望它对某人有帮助。