如何将列表框的selectedItem传递给视图模型



这是一个正在运行的问题,我已经更新了,希望能更清楚一点。简而言之,我试图实现的是将列表框中选定项目的属性传递给视图模型,以便在新查询中使用该属性。在下面的代码中,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));
        }
    }

相关内容

  • 没有找到相关文章

最新更新