简体   繁体   English

如何将信息从一个WPF UserControl传递到另一个WPF UserControl?

[英]How to pass information from one WPF UserControl to another WPF UserControl?

I've got a WPF application. 我有一个WPF应用程序。

On the left side there is a stackpanel full of buttons. 在左侧有一个堆满了按钮的堆叠面板。

On the right side there is an empty dockpanel. 在右侧有一个空的dockpanel。

When user clicks a button , it loads the corresponding UserControl (View) into the dockpanel: 当用户单击按钮时 ,它会将相应的UserControl (View) 加载到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. 这种模式运行良好,因为每个UserControl都是一个View,它有一个ViewModel类,它从模型中获取信息,因此用户可以在页面之间点击,每个页面都可以执行独立的功能,例如编辑所有客户,保存到数据库等

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. 但是,现在,在其中一个页面上,我想要一个ListBox,其中包含Customers列表,每个客户都有一个“编辑”按钮,当单击该编辑按钮时,我想用EditSingleCustomer填充DockPanel UserControl并将其传递给需要编辑的客户。

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 ? 我可以加载EditCustomer用户控件,但是如何将客户编辑并设置其DataContext来编辑该客户呢?

  • 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. 我无法在构造函数中传递它,因为所有UserControl都已创建并存在于MainWindow.xaml.cs中的Dictionary中。
  • 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. 所以我在每个UserControl上创建了一个PrepareUserControl方法并将Customer传递给它,并且可以使用x:Name =“...”后面的代码显示文本框,但这不是重点,我需要DataBind将ItemsControl转换为ObservableCollection当然可以利用WPF的数据绑定功能。
  • so I tried to bind the ListBox ItemSource in the View to its code behind like this: 所以我试图将View中的ListBox ItemSource绑定到它后面的代码,如下所示:

<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. 它获取由该视图中的IntializeComponent()中的无限循环引起的stackoverflow错误。 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: 所以我在想我会以错误的方式解决这个问题,必须有一些更简单的范例来简单地将命令从一个UserControl传递到WPF中的另一个UserControl (在有人说“使用WPF命令”之前,我已经在使用命令了在我的UserControl上,允许用户编辑所有客户,这工作正常,但我必须在我的视图后面的代码(而不是在我的viewmodel中)处理它,因为我需要父窗口上下文能够加载另一个用户控制何时完成保存:

<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? 那么在WPF中我怎么能简单地在DockPanel中加载一个UserControl,在UserControl里面有一个带有命令的按钮,它加载另一个UserControl 并向UserControl发送一个可以绑定其控件的特定对象?

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. 我可以想象我此时对WPF命令知之甚少,如果有人能从这里指出正确的方向,那就太好了,或者如果你认为“在DockPanel模式下加载UserControl对于WPF来说是陌生的”应该避免并用另一种方式来构建应用程序“,这也是有用的新闻。 You can download the current state of my application here to get an idea of how it is structured. 您可以在此处下载我的应用程序的当前状态,以了解它的结构。 Thanks. 谢谢。

This is where you use the Mediator pattern. 这是您使用Mediator模式的地方。 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 ). 关于这个主题有几篇博客文章( 例如 ),并且在一些WPF框架中有模式的实现(例如Prism中的 EventAggregator )。

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: 我刚刚使用WPF完成了一个LOB应用程序,这种问题/模式经常出现,所以这就是我如何解决你的问题:

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. 1)在DataTemplate中,您在ListBox中创建每个项目,以及它的编辑按钮,将Button的标签属性绑定到该列表框项目下面的Customer对象。

2) Create a Click event handler for the button, and set the Button's Click event to fire the handler. 2)为按钮创建Click事件处理程序,并设置Button的Click事件以触发处理程序。

3) In the event handler, set the Content property of the UserControl. 3)在事件处理程序中,设置UserControl的Content属性。

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. 4)在用户控件的范围内设置DataTemplate(可能在其直接容器的资源中),该描述描述该单个客户的编辑器。

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. 另一种方法是在EditCustomer类上声明Customer依赖项属性,然后在单击该按钮时设置该属性(可能通过XAML Trigger)。

I hope this isn't too vague. 我希望这不是太模糊。 If nothing else, know that the problem you're facing is very solvable in WPF. 如果不出意外,请知道您遇到的问题在WPF中是可以解决的。

I don't have the time to really dig into this (it's an interesting question and I hope you get a good answer-- I can see myself running into a similar situation in the future). 我没有时间真正深入研究这个问题(这是一个有趣的问题,我希望你得到一个好的答案 - 我可以看到自己未来遇到类似的情况)。

Have you considered getting a little less WPF-y and falling back to firing an event on your source UserControl with an EventArgs that contains the customer, then in the event handler, firing the appropriate command on the target control? 您是否考虑过少一点WPF-y并回退到使用包含客户的EventArgs在源UserControl上触发事件,然后在事件处理程序中,在目标控件上触发相应的命令?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM