在标准MVVM模式中,viewmodel如何识别InkCanvas中的选定笔划?
在具有InkCanvas知识的代码隐藏中,删除选定的笔划非常简单:
private void btnDeleteSelectedStrokes_Click(object sender, RoutedEventArgs e)
{
StrokeCollection selectedStrokes = theInkCanvas.GetSelectedStrokes();
theInkCanvas.Strokes.Remove(selectedStrokes);
}
但这可以在MVVM中完成吗?
TIA
答案 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
}
}
希望它对某人有帮助。