7
votes

I have a canvas, e.g. similar to this solution or many others using the ItemsControl.

Now I want a button which should be bound to an ICommand. This command should call a method of ViewModel class which can save the image. The saving method is clear, but how do I do the binding following the MVVM pattern?

2
Are you looking for how to bind using MVVM? (MVVM light RelayCommand)is_this_the_way

2 Answers

10
votes

You could pass the Canvas to the ViewModel's Save method using a CommandParameter

<Button Content="Save" 
        Command="{Binding SaveCanvasCommand}" 
        CommandParameter="{Binding ElenementName=myCanvas}" ?>

<Canvas x:Name="myCanvas">
   <!-- Stuff to save -->
</Canvas>

And somewhere in you ViewModel or Command you'd have

void SaveCanvasCommandExecute(object parameter)
{
    UIElement toSave = (UIElement)parameter;
    //.. You'd probably use RenderTargetBitmap here to save toSave.
}
2
votes

If you don't want to reference UI elements in your ViewModel you could use an attached behaviour:

internal static class Behaviours
{
    public static readonly DependencyProperty SaveCanvasProperty =
        DependencyProperty.RegisterAttached("SaveCanvas", typeof(bool), typeof(Behaviours),
                                            new UIPropertyMetadata(false, OnSaveCanvas));

    public static void SetSaveCanvas(DependencyObject obj, bool value)
    {
        obj.SetValue(SaveCanvasProperty, value);
    }

    public static bool GetSaveCanvas(DependencyObject obj)
    {
        return (bool)obj.GetValue(SaveCanvasProperty);
    }

    private static void OnSaveCanvas(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        if ((bool)e.NewValue)
        {
            // Save code.....
        }
    }
}

Then in your ViewModel you have your Command that sets a property, also on your ViewModel:

    public ICommand SaveCanvasCommand
    {
        get
        {
            if (_saveCanvasCommand == null)
                _saveCanvasCommand = new RelayCommand(() => { IsSaveCanvas = true; });

            return _saveCanvasCommand;
        }
    }

And the property which is bound to your View:

    public bool IsSaveCanvas
    {
        get { return _isSaveCanvas; }
        set
        {
            _isSaveCanvas = value;
            RaisePropertyChanged("IsSaveCanvas");
        }
    }

Then hooking it all up in the Xaml looks like this:

Add a Trigger on the Control that binds the value of your ViewModel property to your attached behaviour:

<UserControl.Style>
    <Style>
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsSaveCanvas}" Value="True">
                <Setter Property="wpfApplication1:Behaviours.SaveCanvas" Value="True"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding IsSaveCanvas}" Value="False">
                <Setter Property="wpfApplication1:Behaviours.SaveCanvas" Value="False"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</UserControl.Style>

And then bind your Button / MenuItem to the ViewModels Save Command:

    <Canvas.ContextMenu>
        <MenuItem Header="Save" Command="{Binding SaveCanvasCommand}"/>
    </Canvas.ContextMenu>