【问题标题】:Silverlight how to set the datacontext of a listbox item to the root viewmodel for the view that it is inSilverlight 如何将列表框项的数据上下文设置为其所在视图的根视图模型
【发布时间】:2011-08-12 15:49:47
【问题描述】:

在我的项目中,我有一个使用数据模板的列表框。在这个数据模板中,我有一个按钮。当列表框生成结果时,此列表框的项目源设置为某个属性集合,我们将其称为 Results[0]。

我遇到的问题是,当我单击按钮从视图模型中调用方法时,找不到该方法,因为调用正在查找列表框的上下文而不是根视图。我正在使用 SimpleMVVM 工具包,它使用类似于 MVVMLight 工具包的定位器。

我采用的一种方法是通过在用户控件资源中声明视图模型并静态设置它来显式设置按钮上的数据上下文。

<UserControl.Resources>
    <formatter:HighlightConverter x:Key="FormatConverter" />
    <vml:SearchViewModel x:Key="vm" />
</UserControl.Resources>

然后按钮包含

<HyperlinkButton HorizontalAlignment="Left"
    Click="Button_Click"
    Content="{Binding Type}"
    Style="{StaticResource ListBoxtTitleHyperlink}">
 <i:Interaction.Triggers>
     <i:EventTrigger EventName="Click">
        <ei:CallMethodAction MethodName="GetDetailID" TargetObject="{Binding Source={StaticResource vm}}" />
     </i:EventTrigger>
 </i:Interaction.Triggers>
</HyperlinkButton>

这是因为我现在可以访问该方法,但它实例化了一个新的视图模型,而不是允许我访问视图的根视图模型。因此,我丢失了之前视图模型中可能拥有的任何属性,因此我无法将它们作为参数传递给方法。

我的实现可能在这里关闭。所以我愿意接受建议。 在这种情况下,让列表框数据模板中的按钮调用视图模型中的方法并传递从所选列表框项派生的方法参数的最佳方法是什么?

要查看完整的代码实现,您可以从SkyDrive Folder下载示例项目

更新我开始悬赏这个问题,因为它让我很困惑。随意下载示例项目以供参考。为了清楚起见,这个问题的目的是学习如何完成以下任务 1.从列表框中选择一行 2. selectionchanged事件会设置一个属性为UI中显示的文本值(RecordID双向绑定使用Inotify 3. 单击项目模板中的按钮并使用交互触发器调用存储在 ViewModel 中的方法,并在消息框中显示 RecordID 属性值。

步骤 I 和 2 已完成。我陷入困境的地方是了解如何获取作为列表框项目模板一部分的按钮来定位根视图模型并调用该 VM 的方法,而无需实例化将重置所有先前存储的属性的新 ViewModel。

提前致谢

【问题讨论】:

    标签: silverlight mvvm datatemplate


    【解决方案1】:

    以编程方式添加资源。 StaticResource 绑定可能会在设计时抱怨,但在运行时它应该可以正常工作。

    UserControl 有一个 Resources 属性,它返回对 ResourceDictionary 的引用。您可以将 ViewModel 添加到此,效果将与您的 Xaml 示例相同,只是您可以重用现有的 ViewModel。

    假设您的 MVVM 框架已经使用 ViewModel 填充了 UserControl 的 DataContext,那么您可以使用类似于以下的 C# 代码来设置资源。

    this.Resources.Add("vm", this.DataContext);
    

    如果 DataContext 已经在 UserControl 的构造函数中设置,那么它可以去那里。否则,您将需要在 UserControl 的生命周期中找到稍后调用的钩子。

    编辑:查看了您的代码。我建议进行以下修改。

    1. 不要在 XAML 中设置 DataContext 或“vm”StaticResource。
    2. 使用以下代码作为 TemplateView 类的构造函数。

    代码

    public TemplateView()
    {
        var templateViewModel = new TemplateViewModel();
        this.DataContext = templateViewModel;
        this.Resources.Add("vm", templateViewModel);
        InitializeComponent();
    }
    

    这里有几个限制因素导致我采用了这个解决方案。首先是必须在 InitializeComponent 之前添加资源。二是必须先有templateViewModel可用,才能添加资源。

    【讨论】:

    • 谢谢斯科特。如果我理解您在用户控件构造函数中为列表框或整个控件正确添加资源(数据上下文)?伪代码:this.uc.Resources["vm"]; ?
    • 感谢 Scott 我尝试了这种方法,但客户端无法编译,因为上下文似乎设置了两次。 (从视图和后面的代码中)如果您(或其他任何人)想看一下,我已经上传了一个带有代码的示例项目。 skydrive.live.com/…
    • 我下载了项目,发现编译OK。我没有找到您上面包含的代码。如果您仍然有 DataContext 设置两次时遇到问题的版本,那么我很乐意查看。
    • 对不起那个斯科特。我更新了项目并重新发布了它。我还在 TemplateViewModel.cs 文件中添加了 cmets,以标出相关区域。方法名称不同,但概念相同。再次感谢您的宝贵时间。
    • @randyc:我已经用一些我用来让你的解决方案运行的代码更新了我的答案。
    【解决方案2】:

    我是 WPF 开发人员,不确定这是否适用于 Silverlight,但我通常会将目标对象上的绑定更改为类似

     <ei:CallMethodAction MethodName="GetDetailID" TargetObject="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType=ListBox, Mode=FindAncestor}}"/> 
    

    实际上,查找树直到找到第一个祖先 ListBox,然后检查它的 DataContext 属性,如果我正确阅读了您的问题,它就是您的 ViewModel。

    【讨论】:

    • 谢谢马克,我完全同意。 Silverlight 的问题是它无法访问 Silvlerlight 中的 FindAncestor 模式。它只允许你指向 self 或 templateobject
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-28
    • 2011-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多