print WPF visual from viewmodel

2019-08-28 05:35发布

问题:

how do i print a visual or window from the ViewModel by just calling a method to print without using a RelayCommand as shown in this example: Print WPF Visuals with MVVM pattern?

var printDlg = new PrintDialog();
printDlg.PrintVisual(View, "JOB PRINTING")

回答1:

Ok this is a quick and dirty but reusable attached behavior class. Disclaimer: I just hacked this in a couple of minutes and it has its flaws, which should be easy to overcome.

First we need our service class.

public class VisualPrinter
{
    private static RoutedUICommand PrintVisualCommand = new RoutedUICommand("PrintVisualCommand", "PrintVisualCommand", typeof(VisualPrinter));

    #region ab PrintCommand

    public static ICommand GetPrintCommand(DependencyObject aTarget)
    {
        return (ICommand)aTarget.GetValue(PrintCommandProperty);
    }

    public static void SetPrintCommand(DependencyObject aTarget, ICommand aValue)
    {
        aTarget.SetValue(PrintCommandProperty, aValue);
    }

    public static readonly DependencyProperty PrintCommandProperty =
        DependencyProperty.RegisterAttached("PrintCommand", typeof(ICommand), typeof(VisualPrinter), new FrameworkPropertyMetadata(PrintVisualCommand));

    #endregion

    #region ab EnablePrintCommand

    public static bool GetEnablePrintCommand(DependencyObject aTarget)
    {
        return (bool)aTarget.GetValue(EnablePrintCommandProperty);
    }
    public static void SetEnablePrintCommand(DependencyObject aTarget, bool aValue)
    {
        aTarget.SetValue(EnablePrintCommandProperty, aValue);
    }

    public static readonly DependencyProperty EnablePrintCommandProperty =
        DependencyProperty.RegisterAttached("EnablePrintCommand", typeof(bool), typeof(VisualPrinter), new FrameworkPropertyMetadata(false, OnEnablePrintCommandChanged));

    private static void OnEnablePrintCommandChanged(DependencyObject aDependencyObject, DependencyPropertyChangedEventArgs aArgs)
    {
        var newValue = (bool)aArgs.NewValue;
        var element = aDependencyObject as UIElement;
        if(newValue)
        {
            element.CommandBindings.Add(new CommandBinding(PrintVisualCommand, OnPrintVisual));
        }
    }

    private static void OnPrintVisual(object aSender, ExecutedRoutedEventArgs aE)
    {
        // the element is the one were you set the EnablePrintCommand
        var element = aSender as Visual;
        var printDlg = new PrintDialog();
        // do the printing
    }

    #endregion
}

Here we define two attached properties. PrintCommand is used to inject the actual print command into a view model, this must be done with OneWayToSource. The second, EnablePrintCommand, is to enable the printing, setting which element should be printed and to register the command binding.

My current version has a flaw, that you can't set both properties on the same element, but this can easily be overcome. So to print a a button, it would look like this:

<Grid local:VisualPrinter.EnablePrintCommand="True">
    <Button Content="Test" local:VisualPrinter.PrintCommand="{Binding ViewModelPrint, Mode=OneWayToSource">
</Grid>

While ViewModelPrint is a property of type ICommand in the view model, which can be executed and will then print the grid(therefore the button).