在 ViewModel 中设置时 ListView SelectedItem 未突出显示

Posted

技术标签:

【中文标题】在 ViewModel 中设置时 ListView SelectedItem 未突出显示【英文标题】:ListView SelectedItem not highlighted when set in ViewModel 【发布时间】:2014-11-11 07:32:43 【问题描述】:

我有一个ListView 和一个ItemSource 数据绑定和一个SelectedItem 数据绑定。

每次按下“下一个”或“上一个”按钮时,ListView 都会填充一个新的ItemSource

SelectedItem 相应更新,ItemSource 中的项目具有Selected 状态,因此用户来回导航时可以记住它。

在调试时,一切似乎都运行良好。 VM 按预期更新控件,当我使用下一个和上一个按钮导航时,我还可以看到 ListView 具有正确的选定值。

问题是,无论 ListView 有正确的SelectedItemListView 都不会将SelectedItem 可视化为突出显示。

XAML:

<ListView 
    x:Name="_matchingTvShowsFromOnlineDatabaseListView" 
    Grid.Row="0" 
    Grid.Column="0"
    Grid.RowSpan="3"
    ItemsSource="Binding AvailableMatchingTvShows"
    SelectedItem="Binding AcceptedMatchingTvShow, Mode=TwoWay">
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="Binding Name"/>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

ViewModel 中负责重新填充ItemSourceSelectedItem 的行为:

private void UpdateForCurrentVisibleTvShow()

    var selectedTvShow = FoundTvShows[CurrentTvShow];

    // Update the available matches
    var availableMatchingTvShows = new ObservableCollection<IWebApiTvShow>();
    if (AvailableTvShowMatches[selectedTvShow] != null)
    
        foreach (var webApiTvShow in AvailableTvShowMatches[selectedTvShow])
        
            availableMatchingTvShows.Add(webApiTvShow);
        
    
    AvailableMatchingTvShows = availableMatchingTvShows;

    // Update the selected item
    AcceptedMatchingTvShow = availableMatchingTvShows.FirstOrDefault(webApiTvShow => webApiTvShow.Accepted);

    // Update the progress text
    CurrentTvShowInfoText = string.Format(
        "TV Show: 0 (1 of 2 TV Shows)",
        FoundTvShows[CurrentTvShow],
        CurrentTvShow + 1,
        FoundTvShows.Count);

    // Update the AcceptedMatchingTvShow selection in the listview
    OnPropertyChanged("AcceptedMatchingTvShow");

AcceptedMatchingTvShow的实现:

public IWebApiTvShow AcceptedMatchingTvShow

    get
    
        IWebApiTvShow acceptedTvShow = null;
        if (FoundTvShows.Count > 0)
        
            var tvShowName = FoundTvShows[CurrentTvShow];
            acceptedTvShow = AvailableTvShowMatches[tvShowName].FirstOrDefault(webApiTvShow => webApiTvShow.Accepted);
        
        return acceptedTvShow;
    
    set
    
        if (value != null)
        
            var tvShowName = FoundTvShows[CurrentTvShow];
            var currentlyAcceptedTvShow =
                AvailableTvShowMatches[tvShowName].FirstOrDefault(webApiTvShow => webApiTvShow.Accepted);
            if (currentlyAcceptedTvShow != null)
            
                currentlyAcceptedTvShow.Accepted = false;
            
            value.Accepted = true;
        
        OnPropertyChanged();
    

我希望有人能指出我正确的方向。为了清楚起见,ListView 具有正确的项目,并且 SelectedItem 设置了正确的项目。

【问题讨论】:

this.UpdateLayout(); 后面的代码中试试,你可以在SelectionChanged event 上调用它。您不会以这种方式更改VM 中的任何内容,因此只有View 会受到影响。 HTH 你在哪里设置AcceptedMatchingTvShow @Sinatr 我也用那部分代码更新了问题。它没有需要更新的支持字段,它只需要再次读取属性并评估哪个电视节目是Selected one 你的 OnPropertyChanged();不应该取更改的属性的名称吗? @sexta13,那个OnPropertyChanged方法可能在输入参数上声明了CallerMemberNameAttribute,所以它可以自动访问名称而无需传递它。 【参考方案1】:

嗯,经过大量调试和挖掘,我找到了问题的“解决方案”。我真的很想了解这是否是 WPF 意味着控件的行为方式,或者这是否是 ListViews 数据绑定部分中的错误。如果有人能告诉我,我对正确答案非常好奇(也许我以错误的方式解决了这个问题,有人可以解释我应该如何做到这一点)。

无论如何,当我创建对象的副本时,问题似乎得到了解决:

    public IWebApiTvShow AcceptedMatchingTvShow
    
        get
        
            IWebApiTvShow acceptedTvShow = null;
            if (FoundTvShows.Count > CurrentTvShow)
            
                var tvShowName = FoundTvShows[CurrentTvShow];
                acceptedTvShow = AvailableTvShowMatches[tvShowName].FirstOrDefault(webApiTvShow => webApiTvShow.Accepted);
            

            if (acceptedTvShow != null)
            
                // I MUST create a new instance of the original object for the ListView to update the selected item (why??)
                return new WebApiTvShow(acceptedTvShow);
            
            return null;
        
        set
        
            if (value != null)
            
                var tvShowName = FoundTvShows[CurrentTvShow];
                var availableTvShowMatch = AvailableTvShowMatches[tvShowName];
                var currentlyAcceptedTvShow = availableTvShowMatch.FirstOrDefault(webApiTvShow => webApiTvShow.Accepted);
                if (currentlyAcceptedTvShow != null)
                
                    currentlyAcceptedTvShow.Accepted = false;
                
                value.Accepted = true;
            

            OnPropertyChanged();
        
    

注意对复制构造函数的调用:

return new WebApiTvShow(acceptedTvShow);

它有效,但对我来说似乎真的很荒谬,并且闻起来像 ListView 中的错误。是吗?

我试图在一个更简单的例子here 中解释同样的问题,如果有人可以确认这个错误或者可以解释我应该如何实现它,我将非常感谢你的见解。

【讨论】:

【参考方案2】:

游戏有点晚了,但我一直在用类似的设置跳过箍来解决这个问题。使用 Viewmodel 中的绑定属性或使用绑定 SelectedIndex 的类似属性在 ListView 中设置 SelectedItem 是行不通的。直到我尝试异步执行:

Task.Factory.StartNew(() =>
   
       BoundSelectedIndex = index;
   );

似乎有效 - 更高级的贡献者可能会回答为什么...

【讨论】:

以上是关于在 ViewModel 中设置时 ListView SelectedItem 未突出显示的主要内容,如果未能解决你的问题,请参考以下文章

在 UINavigationController 中设置时图像不显示

在情节提要中设置时,UISearchBar 不显示范围栏

在通知回调中设置时,标签不会出现在标签栏项目上

WPF CommandParameter 在上下文菜单中设置时未更新

在 linux 机器中设置时的 Sonarqube-4.0 问题 (linux-x86-32)

当我最初在 java 或 Kotlin 中设置时,CheckBox 的 isChecked 不起作用