-1

I have

  • MainView
  • MenuView
  • ViewModel
  • ContentView

MainView is Window, which include MenuView & ContentView as UserControl.
MenuView has a print button, which bind to ViewModel's print command.
ViewModel has a print command, which need RichTextBox's FlowDocument in order to print.
ContentView has a RichTextBox, which is needed to print.

I can bind MenuView and ViewModel. However, I can not get a ContentView's FlowDocument.

I try to set ContentView's DataContext to ViewModel's FlowDocument (which may be a violation of MVVM pattern), but it fails because RichTextBox's Document can not bind to ViewModel's FlowDocument.

Therefore, I need a help to design MVVM pattern for printing RichTextBox's FlowDocument.

Here is simplified sample code,

ViewModel.cs

class ViewModel
{
     public ICommand PrintCommand;
     //Fail binding to ContentView's RichTextBox
     public FlowDocument FlowDocument;

     public ViewModel 
     {
         PrintCommand = new PrintCommand();
     }
}

MenuView.xaml

<Button x:Name="ButtonPrint" Command="{Binding PrintCommand}" 
 CommandParameter="/* How to get ContentView's FlowDocument */" >

OR

MenuView.xaml.cs

private void ButtonPrint_Click(object sender, RoutedEventArgs e)
{
    (this.DataContext as MainViewModel).PrintCommand.Execute(/* How to get ContentView's FlowDocument */);
}

ContentView.xaml

<RichTextBox>
    <FlowDocument x:Name="FlowDocument">
        <Paragraph>
            <Run Text="RichTextBox"/>
        </Paragraph>
    </FlowDocument>
</RichTextBox>

PrintCommand.cs

class PrintCommand : ICommand
{
    private FlowDocument flowDocument; 
    public PrintCommand(FlowDocument flowDocument)
    {
        this.flowDocument = flowDocument;
    }

    public void Execute(object parameter)
    {
        //Execute Print!
        Console.Write("Print!");
    }
}
ibocon
  • 1,352
  • 3
  • 17
  • 40
  • 3
    It is a matter of opinion as to how to design the code. However, it seems reasonable to me for the view model to abstract the command only (i.e. the user interaction receiving the command) and have some mechanism like an event for the view (which owns and knows about the flow document to print) to subscribe to and handle itself. Printing is more of a "view" thing, not a "data" thing, so it makes sense to me that it would be handled in view code, not model code. – Peter Duniho Dec 13 '17 at 06:17
  • @PeterDuniho I understand your opinion is reasonable. but my project has a complecated printing process because of custom printer (nemonic printer). I need to rotate paper in 4 directions, set custom paper size to print, set paper margin and record print count in order to notify user to clean printer header. Therefore I decide to manipulate printing process in ViewModel, not in View. Printing process need some properties, which are set by other view. – ibocon Dec 13 '17 at 06:42
  • Have a look here https://stackoverflow.com/a/28815689/2389106 . If you will design this or similar way, you should have an access to your FlowDocument – Sasha Dec 13 '17 at 07:53
  • @Sasha I amnot trying to create **MenuViewModel** and **ContentViewModel**. I have a only one ViewModel. The problem is Event is fired at **MenuView** but I need **ContentView's FlowDocument** in order to print content. (Maybe what I tried could mislead your understanding. Sorry for that) – ibocon Dec 13 '17 at 08:12
  • @ibocon This link shows the way to work with Vm and Views, not how to implement MenuViewModel or MenuView... As for your question it is not clear, how is implemented ContentView.xaml. Is it UserControl inside window(MenuView)? If yes, how do you load it to view? – Sasha Dec 13 '17 at 08:16
  • Why do you use both ButtonPrint_Click() in codebehind and PrintCommand in viewModel. If you will use PrintCommand from viewModel, then you have access to your FlowDocument – Sasha Dec 13 '17 at 08:30
  • @Sasha You are right! I forget to mention MenuView is _UserControl_. Sorry for that. So, strcutre is like MainView(Window) has two UserControl, _MenuView_ and _ContentView_. And ButtonPrint_Click() is just an example. NOT how I will implement it or implemented. – ibocon Dec 13 '17 at 08:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/161090/discussion-between-sasha-and-ibocon). – Sasha Dec 13 '17 at 08:39

1 Answers1

2

if I understood correctly you may do the following.

I just give a quick try.

On your ContentControl you declare a read only dependency property.

    public ContentView()
    {
        InitializeComponent();
        this.FlowDocument = this.RealFlowDocument;
    }


    private static readonly DependencyPropertyKey FlowDocumentPropertyKey =
        DependencyProperty.RegisterReadOnly("FlowDocument", typeof(FlowDocument), typeof(ContentView),
            new FrameworkPropertyMetadata());


    public static readonly DependencyProperty FlowDocumentProperty
        = FlowDocumentPropertyKey.DependencyProperty;

    public FlowDocument FlowDocument
    {
        get { return (FlowDocument)GetValue(FlowDocumentProperty); }
        protected set { SetValue(FlowDocumentPropertyKey, value); }
    }

In your MenuView, you do something similar (not read only)

    private static readonly DependencyProperty FlowDocumentProperty =
        DependencyProperty.Register("FlowDocument", typeof(FlowDocument), typeof(MenuView),
            new FrameworkPropertyMetadata());


    public FlowDocument FlowDocument
    {
        get { return (FlowDocument)GetValue(FlowDocumentProperty); }
        set { SetValue(FlowDocumentProperty, value); }
    }

Finally, you can connect both in your MainView

<Grid>
    <local:ContentView x:Name="contentView"/>
    <local:MenuView FlowDocument="{Binding ElementName=contentView, Path=FlowDocument, Mode=OneWay}"/>
</Grid>

And you can bind in your MenuView to the FlowDocument as CommandParamater or use it on button click.

ogomrub
  • 146
  • 2
  • 7