这是一个正在运行的问题,我已经更新了,希望能更清楚一点。简而言之,我试图实现的是将列表框中选定项目的属性传递给视图模型,以便在新查询中使用该属性。在下面的代码中,Listbox从父对象继承数据绑定。列表框包含用于呈现详细结果的数据模板(用户控件)。
我遇到的问题是,在用户控件中,我有一个扩展器,当单击它时,它会调用ViewModel中的命令。从我所看到的情况来看,Listbox对象正在失去它的数据上下文,因此为了在扩展程序展开时调用命令,我必须显式设置扩展程序的数据上下文。这样做似乎实例化了一个新的视图模型,它将我的绑定属性(SelectedItemsID)重置为null。
当按钮从模板列表框项目中调用命令时,是否有方法将所选项目从视图传递到视图模型,并防止值重置为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;
}
}
listbox模板包含一个带有扩展器控件的自定义布局。扩展器控件用于显示与所选项目相关的更多详细信息。这些详细信息(集合)是通过对我的代理进行新调用创建的。为了使用扩展器控件实现这一点,我使用了表达式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);}
我遇到的问题是,当选择列表框项目时,selectionchanged事件会触发,并且属性SelectedItemID会更新为所选项目的正确id。当我单击扩展器时,命令会被激发,但属性SelectedItemID设置为null。我已经用Silverlight Spy追踪到了这一点,事件与您所期望的一致,当单击扩展器时,列表框项目失去焦点,扩展器(切换)获得焦点,并且存在LeftMouseDownEvent,但我看不到任何事情可以解释为什么该属性被设置为null。我将select changed事件中使用的相同代码添加到listboxt项目上的LostFocus事件中,仍然收到相同的结果。
如果您能理解为什么当列表框控件中的扩展器按钮设置为null时,公共属性SelectedItemID被设置为null,我将不胜感激。当然,如果您能帮助我学习如何防止属性设置为null并保留绑定ID,我将不胜感激。
更新我试图从扩展器中删除数据上下文引用,因为这被认为是问题所在。根据我所拥有的,因为这是一个数据模板项,它"走出"了可视化树,并失去了对从父对象继承的控件的数据上下文的引用。如果我尝试在控件的代码中设置datacontext,那么所有到属性的绑定都将丢失。
我的下一个尝试是将构造函数中扩展器控件的数据上下文设置为
private SearchViewModel _ViewModel;
public srchCFS()
{
InitializeComponent();
this.cfsExpander.DataContext = this._ViewModel;
}
这种方法似乎不起作用,因为InvokeCommandAction从未被激发。只有在扩展器上设置了数据上下文时,此命令才会触发。
提前感谢
使用此行,您可以使用其默认构造函数创建一个新的SearchViewModelDataSource。
DataContext="{Binding Source={StaticResource SearchViewModelDataSource}}"
我想这就是为什么你会发现null,因为这是引用类型的默认值。您可以通过将DataContext设置为与主控件相同的实例来解决此问题(您可以在初始化所有组件后通过代码来完成此操作)。
希望得到帮助!
编辑
我不认为从代码中设置datacontext后绑定可能会丢失。每当我需要在两个或多个模型之间共享一些东西时,我都会这样做
关于你写的代码:
private SearchViewModel _ViewModel;
public srchCFS()
{
InitializeComponent();
this.cfsExpander.DataContext = this._ViewModel;
}
您可以尝试使用FindName方法,而不是使用this.cfsExpander。也许这会给您返回正确的实例。
object item = this.FindName("expander_name");
if ((item!=null)&&(item is Expander))
{
Expander exp = item as Expander;
exp.DataContext = this._ViewModel;
}
试试它是否对你有用
当然,这个_ViewModel必须公开一个名为GetCfsResultCommand的ICommand类型的属性,但我认为这已经完成了。
虽然这是一种很难的方法,但我找到了一个中间解决方案,可以将列表框项值添加到视图模型中。我最终使用了selection-changed事件,并将值直接传递给视图模型中的公共属性。不是最好的方法,但它解决了问题短期
private void ResultListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (((ListBox)sender).SelectedItem != null)
_ViewModel.SelectedItemID = (((ListBox)sender).SelectedItem as QueryResult).ID.ToString();
MySelectedValue = (((ListBox)sender).SelectedItem as QueryResult).ID.ToString();
this.NotifyPropertyChanged(_ViewModel.SelectedItemID);
}
为了激发这一点,我还必须在视图中设置一个属性更改处理程序,以将更改推送到VM。您可以忽略MySelectedValue行,因为它是用于测试的辅助代码。
对于那些插入的通用属性更改处理程序
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}