【发布时间】:2011-09-10 06:27:53
【问题描述】:
这是一个运行中的问题,我已对其进行了更新,希望能更清楚一点。 简而言之,我想要完成的是将一个属性从列表框选定项传递给视图模型,以便可以在新查询中使用该属性。在下面的代码中,列表框从父对象继承数据绑定。列表框包含用于呈现详细结果的数据模板(用户控件)。
我遇到的问题是,在用户控件中,我有一个扩展器,单击该扩展器会从 ViewModel 调用命令。从我可以看到 Listbox 对象正在失去它的数据上下文,因此为了在扩展器展开时调用命令,我必须显式设置扩展器的数据上下文。这样做似乎会实例化一个新的视图模型,它将我的绑定属性 (SelectedItemsID) 重置为 null。
当按钮从模板列表框项目中调用命令时,有没有办法将选定项目从视图传递到视图模型并防止值被重置为空?
我意识到 Prism 和 MVVMLite 都有解决此问题的方法,但我对这两个框架都不熟悉,所以我不知道将其中任何一个应用到我的项目中的复杂程度。
这可以在 Prism 或 MVVMLite 之外完成吗?
原帖如下:
在我的项目中,我有一个包含自定义数据模板的列表框用户控件。
<ListBox x:Name="ResultListBox"
HorizontalAlignment="Stretch"
Background="{x:Null}"
BorderThickness="0"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding SearchResults[0].Results,
Mode=TwoWay}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectionChanged="ResultListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<dts:TypeTemplateSelector Content="{Binding}" HorizontalContentAlignment="Stretch">
<!-- CFS Template -->
<dts:TypeTemplateSelector.CFSTemplate>
<DataTemplate>
<qr:srchCFS />
</DataTemplate>
</dts:TypeTemplateSelector.CFSTemplate>
<!-- Person Template -->
<dts:TypeTemplateSelector.PersonTemplate>
<DataTemplate>
<qr:srchPerson />
</DataTemplate>
</dts:TypeTemplateSelector.PersonTemplate>
<!-- removed for brevity -->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
SelectionChanged 从后面的代码调用下面的方法
private void ResultListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (((ListBox)sender).SelectedItem != null)
_ViewModel.SelectedItemID = (((ListBox)sender).SelectedItem as QueryResult).ID.ToString();
this.NotifyPropertyChanged(_ViewModel.SelectedItemID);//binds to VM
}
在 ViewModel 中我有以下属性
public string SelectedItemID
{
get
{
return this._SelectedItemID;
}
set
{
if (this._SelectedItemID == value)
return;
this._SelectedItemID = value;
}
}
列表框模板包含一个带有扩展控件的自定义布局。扩展器控件用于显示与所选项目相关的更多详细信息。这些详细信息(集合)是通过对我的代理进行新调用来创建的。要使用扩展器控件执行此操作,我使用了 Expressions InvokeCommandAction
<toolkit:Expander Height="auto"
Margin="0,0,-2,0"
Foreground="#FFFFC21C"
Header="View Details"
IsExpanded="False"
DataContext="{Binding Source={StaticResource SearchViewModelDataSource}}"
Style="{StaticResource DetailExpander}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Expanded">
<i:InvokeCommandAction Command="{Binding GetCfsResultCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
在 ViewModel 中,调用的委托命令 GetCFSResultCommandExecute 非常简单
private void GetCfsResultCommandExecute(object parameter)
{
long IdResult;
if (long.TryParse(SelectedItemID, out IdResult))
{
this.CallForServiceResults = this._DataModel.GetCFSResults(IdResult);}
我遇到的问题是在选择listbox项目时,选择中间的事件触发和属性selectedItemid从所选项目的正确ID更新。当我单击扩展器时,会触发 Command 但属性 SelectedItemID 设置为 null。我已经用 Silverlight-Spy 跟踪了这一点,事件与单击扩展器时您所期望的一致该属性被设置为空。我将选择更改事件中使用的相同代码添加到 listboxt 项上的 LostFocus 事件中,但仍然收到相同的结果。
当作为列表框控件一部分的展开器按钮设置为空时,我将不胜感激理解为什么将公共属性 SelectedItemID 设置为空。当然,在学习如何防止将属性设置为 null 并保留绑定 ID 方面,我非常感谢任何帮助。
更新 我试图从 Expander 中删除 datacontext 引用,因为这被认为是问题所在。从我所拥有的,因为这是一个数据模板项,它“步”出可视化树并失去对从父对象继承的控件的数据上下文的引用。如果我尝试在代码中为控件设置数据上下文,则所有与属性的绑定都会丢失。
我的下一个尝试是将构造函数中扩展器控件的数据上下文设置为
private SearchViewModel _ViewModel;
public srchCFS()
{
InitializeComponent();
this.cfsExpander.DataContext = this._ViewModel;
}
这种方法似乎不起作用,因为 InvokeCommandAction 从未被触发。该命令似乎仅在扩展器上设置了数据上下文时触发。
提前致谢
【问题讨论】:
-
你比它需要的更难很多。只需将 SelectedItem 绑定回 ViewModel 并从该项目中获取 Id。尝试将 SelectedItem 绑定到 Model 的属性将需要您做大量的提升,因为 ItemsControl 期望 SelectedItem 是其 ItemsSrouce 属性中的一个实例,而不是其中一个实例的 property。
-
@Will,谢谢我会尝试这个,但它似乎没有工作 SelectedItem="{Binding SelectedItemID, Mode=TwoWay}" 我假设这可能是由于列表框项目是用户由一组属性组成的控件?
-
SearchResults[0].Results是 X 类型的集合。您在 ViewModel 上公开了 X 类型的 DP,称之为SelectedX。然后将它绑定到SelectedItem="{Binding SelectedX}",这样当您选择列表中的一项时,该项目就会在SelectedX中找到。然后,您可以通过SelectedX.Id获取该 ID。
标签: silverlight data-binding mvvm expression-blend inotifypropertychanged