Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass information from one WPF UserControl to another WPF UserControl?

I've got a WPF application.

On the left side there is a stackpanel full of buttons.

On the right side there is an empty dockpanel.

When user clicks a button, it loads the corresponding UserControl (View) into the dockpanel:

private void btnGeneralClick(object sender, System.Windows.RoutedEventArgs e)
{
    PanelMainContent.Children.Clear();
    Button button = (Button)e.OriginalSource;
    Type type = this.GetType();
    Assembly assembly = type.Assembly;

    IBaseView userControl = UserControls[button.Tag.ToString()] as IBaseView;
    userControl.SetDataContext();

    PanelMainContent.Children.Add(userControl as UserControl);
}

This pattern works well since each UserControl is a View which has a ViewModel class which feeds it information which it gets from the Model, so the user can click from page to page and each page can carry out isolated functionality, such as editing all customers, saving to the database, etc.

Problem:

However, now, on one of these pages I want to have a ListBox with a list of Customers in it, and each customer has an "edit" button, and when that edit button is clicked, I want to fill the DockPanel with the EditSingleCustomer UserControl and pass it the Customer that it needs to edit.

I can load the EditCustomer usercontrol, but how do I pass it the customer to edit and set up its DataContext to edit that customer?

  • I can't pass it in the constructor since all the UserControls are already created and exist in a Dictionary in the MainWindow.xaml.cs.
  • so I created a PrepareUserControl method on each UserControl and pass the Customer to it and can display it with a textbox from code behind with x:Name="..." but that is not the point, I need to DataBind an ItemsControl to a ObservableCollection to take advantage of WPF's databinding functionality of course.
  • so I tried to bind the ListBox ItemSource in the View to its code behind like this:

<UserControl.Resources>
    <local:ManageSingleCustomer x:Key="CustomersDataProvider"/>
</UserControl.Resources>

<DockPanel>
    <ListBox ItemsSource="{Binding Path=CurrentCustomersBeingEdited, Source={StaticResource CustomersDataProvider}}"
             ItemTemplate="{DynamicResource allCustomersDataTemplate}"
             Style="{DynamicResource allCustomersListBox}">
    </ListBox>
</DockPanel>

which gets a stackoverflow error caused by an endless loop in the IntializeComponent() in that view. So I'm thinking I'm going about this in the wrong way, there must be some easier paradigm to simply pass commands from one UserControl to another UserControl in WPF (and before someone says "use WPF commanding", I already am using commanding on my UserControl that allows the user to edit all customers, which works fine, but I have to handle it in my code behind of my view (instead of in my viewmodel) since I need the parent window context to be able to load another user control when its finished saving:

<Button Style="{StaticResource formButton}" 
        Content="Save" 
        Command="local:Commands.SaveCustomer"
        CommandParameter="{Binding}"/>

private void OnSave(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
{
    Customer customer = e.Parameter as Customer;
    Customer.Save(customer);

    MainWindow parentShell = Window.GetWindow(this) as MainWindow;
    Button btnCustomers = parentShell.FindName("btnCustomers") as Button;
    btnCustomers.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
}

So how in WPF can I simply have a UserControl loaded in a DockPanel, inside that UserControl a button with a command on it that loads another UserControl and sends that UserControl a specific object to which it can bind its controls?

I can imagine I just do not know enough about WPF commands at this point, if anyone can point me in the right direction from here, that would be great, or if you think this "loading UserControls in a DockPanel pattern is foreign to WPF and should be avoided and replaced with another way to structure applications", that would be helpful news as well. You can download the current state of my application here to get an idea of how it is structured. Thanks.

like image 914
Edward Tanguay Avatar asked May 08 '09 14:05

Edward Tanguay


People also ask

How pass data from one user control to another in WPF?

eg: UserControl1 is displayed within WPFWindow1 UserControl2 is displayed within WPFWindow2 MainWindow opens Window1 , Window1 opens Window 2 . So the requirement is that in Window2 , when the User clicked the button it should pass a value to Window1 . The proper way to do this is with databinding to a 'Model' class.

What is the difference between UserControl and window in WPF?

A window is managed by the OS and is placed on the desktop. A UserControl is managed by wpf and is placed in a Window or in another UserControl. Applcations could be created by have a single Window and displaying lots of UserControls in that Window.

Can we have multiple user controls in WPF?

Adding Multiple Controls to the Content Panel | Basic Library for WPF and Silverlight | ComponentOne. You cannot set the Content property to more than one control at a time.

What is UserControl WPF?

User controls, in WPF represented by the UserControl class, is the concept of grouping markup and code into a reusable container, so that the same interface, with the same functionality, can be used in several different places and even across several applications.


2 Answers

I've just finished a LOB application using WPF where this sort of problem/pattern appeared constantly, so here's how I would have solved your problem:

1) In the DataTemplate where you create each item in the ListBox, along with it's edit button, bind the Button's tag property to the Customer object underlying that list box item.

2) Create a Click event handler for the button, and set the Button's Click event to fire the handler.

3) In the event handler, set the Content property of the UserControl.

4) Set up a DataTemplate in scope of the User Control (perhaps in the resources of it's immediate container) which describes an editor for that single customer.

Another approach that will work is to declare a Customer dependency property on your EditCustomer class, then set that property (perhaps through a XAML Trigger) when the button is clicked.

I hope this isn't too vague. If nothing else, know that the problem you're facing is very solvable in WPF.

like image 98
Mark Avatar answered Oct 06 '22 14:10

Mark


This is where you use the Mediator pattern. There's several blog posts on this topic (for instance), and there's implementations of the pattern in some WPF frameworks (such as EventAggregator in Prism).

like image 21
wekempf Avatar answered Oct 06 '22 14:10

wekempf