5

我有一个 WPF 应用程序。

在左侧有一个堆栈面板,里面装满了按钮。

右侧有一个空的停靠面板。

当用户单击按钮时,它会将相应的 UserControl (View) 加载到停靠面板中:

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);
}

这种模式效果很好,因为每个 UserControl 都是一个 View,它有一个 ViewModel 类,该类向它提供从 Model 获得的信息,因此用户可以从一个页面点击到另一个页面,每个页面都可以执行独立的功能,例如编辑所有客户,保存到数据库等

问题:

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

我可以加载 EditCustomer 用户控件,但是如何将它传递给客户进行编辑并设置其 DataContext 来编辑该客户

  • 我无法在构造函数中传递它,因为所有 UserControls 都已创建并存在于 MainWindow.xaml.cs 的 Dictionary 中。
  • 所以我在每个 UserControl 上创建了一个 PrepareUserControl 方法,并将 Customer 传递给它,并且可以使用 x:Name="..." 后面的代码中的文本框显示它,但这不是重点,我需要将 ItemsControl 数据绑定到一个ObservableCollection 当然可以利用 WPF 的数据绑定功能。
  • 所以我尝试将 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>

这会导致该视图中 IntializeComponent() 中的无限循环导致堆栈溢出错误。所以我想我以错误的方式解决这个问题,必须有一些更简单的范例来简单地将命令从一个 UserControl 传递到 WPF 中的另一个 UserControl(在有人说“使用 WPF 命令”之前,我已经在使用命令在我的 UserControl 上允许用户编辑所有客户,这工作正常,但我必须在我的视图后面的代码中处理它(而不是在我的视图模型中),因为我需要父窗口上下文才能加载另一个用户控制何时完成保存:

<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));
}

那么如何在 WPF 中简单地将一个 UserControl 加载到 DockPanel 中,在该 UserControl 内部有一个带有命令的按钮,该按钮加载另一个 UserControl并向该 UserControl 发送一个可以绑定其控件的特定对象?

我可以想象此时我对 WPF 命令的了解还不够,如果有人能从这里指出我正确的方向,那就太好了,或者如果您认为这种“在 DockPanel 模式中加载 UserControls 对 WPF 来说是陌生的,并且应该避免并用另一种构建应用程序的方式代替”,这也是有用的消息。您可以在此处下载我的应用程序的当前状态,以了解它的结构。谢谢。

4

3 回答 3

4

我刚刚使用 WPF 完成了一个 LOB 应用程序,其中这种问题/模式不断出现,所以我将如何解决您的问题:

1) 在您创建 ListBox 中的每个项目的 DataTemplate 中,连同它的编辑按钮,将 Button 的 tag 属性绑定到该列表框项目下的 Customer 对象。

2) 为按钮创建一个 Click 事件处理程序,并设置 Button 的 Click 事件以触发该处理程序。

3) 在事件处理程序中,设置 UserControl 的 Content 属性。

4) 在用户控件范围内(可能在其直接容器的资源中)设置一个 DataTemplate,它描述了该单个客户的编辑器。

另一种可行的方法是在 EditCustomer 类上声明 Customer 依赖属性,然后在单击按钮时设置该属性(可能通过 XAML 触发器)。

我希望这不是太模糊。如果不出意外,请知道您面临的问题在 WPF 中是可以解决的。

于 2009-05-12T18:07:20.847 回答
3

这是您使用中介者模式的地方。有几篇关于这个主题的博客文章(例如),并且在一些 WPF 框架(例如Prism中的EventAggregator )中有该模式的实现。

于 2009-05-08T15:59:47.773 回答
1

我没有时间真正深入研究这个(这是一个有趣的问题,我希望你能得到一个好的答案——我可以看到自己将来会遇到类似的情况)。

您是否考虑过减少 WPF-y 并回退到使用包含客户的 EventArgs 在源 UserControl 上触发事件,然后在事件处理程序中触发目标控件上的适当命令?

于 2009-05-08T15:09:15.070 回答