bene_rawr
bene_rawr

Reputation: 119

How to bind data from View to UserControl with SoapBox

I have my SoapBox.Document 'Register'

[Export(SoapBox.Core.ExtensionPoints.Workbench.Documents, typeof(IDocument))]
[Export(CompositionPoints.Workbench.Documents.Register, typeof(Register))]
[Document(Name = DOC_NAME)]

class Register : AbstractDocument
{
    public Receipt actualReceipt;
    private const string DOC_NAME = "Register";
    public Register()
    {
        Name = DOC_NAME;
        Title = "Recipe Document Title";
        SomeProperty = "Hello from the recipe document!";
    }
}

In this Document I want to user UserControls which are kind of a own "View" Like a ListView for all ReceiptPositions

So now I got my Model Receipt and ReceiptPosition

Model Receipt

class Receipt
{
    public int Id { get; set; }
    public string Receiptnumber { get; set; }
    public IList<ReceiptPositions> ReceiptPositions { get; set; }

and Model ReceiptPosition

class ReceiptPosition
{
    public int Id { get; set; }
    //public Receipt Receipt { get; set; } using for Database 
    public int Position { get; set; }
    public string Article { get; set; }
}

So now I want to add a UserControl witch displays a List of all articles in ReceiptPositions.

But how do I bind the data so that when a new ReceiptPosition gets added to the IList in Receipt the UserControl get 'refreshed' automatically?

Here is a visual example of what I need..

Host with Data and two Plugins which each show the same Data but in a different way.

Upvotes: 1

Views: 97

Answers (2)

Mark Feldman
Mark Feldman

Reputation: 16138

Death's answer is correct, i.e. you use DataTemplates. If your views and data templates are in a MEF plugin then you need to import both the plugins and the data templates that map the view models to the views. In the other question you posted about this it was obvious that you're trying to export your plugin user controls...personally I think this is a bit misguided. If your main application is using MVVM then your plugins should as well. In this case your plugins should export an IPlugin class and also specify a DataTemplate that maps it to a view. As I indicated on the other page, the data template must be imported as well so that you can add it to the global resources.

I've created a project that shows this in action using the classes you provided in your uother question, you can download it here. The main points to look at are the data templates in the plugin project and the two places where things are imported in the main project.

enter image description here

Note that in my demo I'm requiring each plugin to explicitly specify a DataTemplate for its view and view model, but you may not want to do this so I've also added a chunk of commented-out code at the bottom of App.xaml.cs that shows how to avoid that (to make it work I had to add the view type to the IPlugData class, but that's only needed for this one example). If you choose to create the DataTemplates manually then the plugins don't need to specify the data templates and they also don't need the custom ResourceDictionary that holds them.

If you have an questions feel free to post back here in the comments.

Upvotes: 0

DeathTails
DeathTails

Reputation: 466

You can use an ItemsControl for this purpose.

xaml:

<ItemsControl ItemsSource="{Binding MyReceipt.ReceiptPositions}">

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <!-- Where you put your view -->
            <TextBox Text="{Binding Article}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>

    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <!-- Can be whatever Panel type you want -->
            <StackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

</ItemsControl>

cs:

private Receipt _myReceipt;
public Receipt MyReceipt { get { return _myReceipt; } set { _myReceipt = value; OnPropertyChanged("MyReceipt"); } } 

public MainWindow()
{
    InitializeComponent();
    DataContext = this;
    MyReceipt = new Receipt { ReceiptPositions = new ObservableCollection<ReceiptPosition>() };

    MyReceipt.ReceiptPositions.Add(new ReceiptPosition { Article = "Foo" });
    MyReceipt.ReceiptPositions.Add(new ReceiptPosition { Article = "Bar" });
    MyReceipt.ReceiptPositions.Add(new ReceiptPosition { Article = "Baz" });

    MyReceipt.ReceiptPositions[0].Article = "Frabazamataz";
}

Explanation:

The ItemsControl allows you to bind a list to its ItemsSource Property to use as the DataContext to each view created by the DataTemplate.

The Observable Collection gives PropertyChange notifications automatically with each item added, removed, or changed.

This allows you to have a very flexible list of items based solely on your data.

Upvotes: 1

Related Questions