Travis Su
Travis Su

Reputation: 709

How to pass data from MainWindow to a User Control that's inside the MainWindow?

I did my research that people tend to use ViewModel to achieve this but I am sort of stuck in it.

I have a

public ObservableCollection<Order> orderList { get; set; } = new ObservableCollection<Order>();

in MainWindow which is already filled up with data.

in MainWindow XAML I have a User Control inside the TabControl:

<TabControl x:Name="TabCollection">
        <TabItem Header="UC1">
            <local:UserControl1/>
        </TabItem>
        <TabItem Header="UC2">
            <local:UserControl2/>
        </TabItem>
    </TabControl>

We only talk about UC1 here so in UC1 XAML here I have a ListView inside:

    <UserControl.DataContext>
    <local:UserControl1VM/>
</UserControl.DataContext>

<ListView x:Name="ListViewText">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="First name" DisplayMemberBinding="{Binding Firstname}"/>
            <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding Lastname}"/>
            <GridViewColumn Header="Order" DisplayMemberBinding="{Binding Ordername}"/>
            <GridViewColumn Header="Delivery time" DisplayMemberBinding="{Binding Deliverytime}"/>
            <GridViewColumn Header="Phone Number" DisplayMemberBinding="{Binding Phone}"/>
            <GridViewColumn Header="Address" DisplayMemberBinding="{Binding Address}"/>
            <GridViewColumn Header="Email" DisplayMemberBinding="{Binding Email}"/>
        </GridView>
    </ListView.View>
</ListView>

And here's the code in UserControl1VM.cs:

namespace QuickShop
{
class UserControl1VM : INotifyPropertyChanged
{
    private ObservableCollection<Order> orderList;
    public ObservableCollection<Order> OrderList
    {
        get { return orderList; }
        set
        {
            orderList = value;
            PropertyChanged(this, new PropertyChangedEventArgs("OrderList"));
        }
    }

    //
    private void FindDeliveryOrders(IEnumerable<Order> sortList)
    {
        foreach (var order in sortList)
        {
            if (order.Delivery.Equals("Yes"))
            {
                //deliveryOrders.Add(order);
                this.ListViewText.Items.Add(new Order { Firstname = order.Firstname, Lastname = order.Lastname, Ordername = order.Ordername, Deliverytime = order.Deliverytime, Phone = order.Phone, Address = order.Address, Email = order.Email });
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
}

And Of course these are incomplete codes because I don't know how to proceed next.

My goal is just to populate the ListView and it will automatically update itself if orderList changes. But right now I couldn't even know whether the ViewModel is working or not, any thoughts and code demo would be very grateful.

Upvotes: 2

Views: 1953

Answers (1)

Clemens
Clemens

Reputation: 128136

A UserControl should never have a "private" view model, as you assign it to the DataContext in the UserControl's XAML. It should instead expose dependency properties that could be bound to properties of an externally provided view model object.

Declare an ItemsSource property like this:

public partial class UserControl1 : UserControl
{
    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register(
            nameof(ItemsSource), typeof(IEnumerable), typeof(UserControl1));

    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public UserControl1()
    {
        InitializeComponent();
    }
}

And bind the ListView like this:

<UserControl ...>
    ...
    <ListView ItemsSource="{Binding ItemsSource,
                            RelativeSource={RelativeSource AncestorType=UserControl}}">
        ...
    </ListView>
    ...
</UserControl>

When you use the UserControl, bind the property to a view model property:

<TabItem Header="UC1">
    <local:UserControl1 ItemsSource="{Binding OrderList}"/>
</TabItem>

The last XAML snippet assumes that the object in the UserControl's DataContext has a OrderList property. This would automatically happen when the TabControl is bound to a collection of view model objects with that property.


Alternatively, let the elements in the UserControl's XAML directly bind to the properties of the object in the inherited DataContext.

<UserControl ...>
    ...
    <ListView ItemsSource="{Binding OrderList}">
        ...
    </ListView>
    ...
</UserControl>

Your control would not have to expose additional bindable properties, but it would only work with DataContext objects that actually provide the expected source properties.

Upvotes: 2

Related Questions