使用 MVVM 重置组合框选定项目

Posted

技术标签:

【中文标题】使用 MVVM 重置组合框选定项目【英文标题】:Reset combobox selected item on set using MVVM 【发布时间】:2013-07-25 03:28:44 【问题描述】:

我在我的 WPF 应用程序中使用 ComboBox 并遵循 MVVM。我想在 ComboBox 中显示一个字符串列表。

XAML:

<ComboBox ItemsSource="Binding ItemsCollection" SelectedItem="Binding SelectedItem" />

查看模型:

public Collection<string> ItemsCollection; // Suppose this has 10 values.
private string _selectedItem;
public string SelectedItem

    get  return _selectedItem; 
    set
    
        _selectedItem = value;
        Trigger Notify of property changed.
    

现在这段代码运行良好。我可以从视图中进行选择,并且可以在 ViewModel 中进行更改,如果我从 ViewModel 更改 SelectedItem,我可以在我的视图中看到它。

现在这就是我想要实现的目标。当我从我的视图中更改所选项目时,我需要检查值是好/坏(或任何东西)设置所选项目,否则不要设置它。所以我的视图模型变成了这样。

public string SelectedItem

    get  return _selectedItem; 
    set
    
        if (SomeCondition(value))
            _selectedItem = value;           // Update selected item.
        else
            _selectedItem = _selectedItem;   // Do not update selected item.
        Trigger Notify of property changed.
    

现在,当我执行此代码并且 SomeCondition(value) 返回 false 时,SelectedItem 返回旧字符串值,但在我看来,ComboBox 中的选定项是我选择的值。因此,假设我的 ComboBox 中显示了 10 个字符串的集合。除第二个和第四个元素外,所有值都很好(SomeCondition 对第二个和第四个值返回 false)。如果我选择第二个或第四个元素 selectedItem ,我想要什么不要改变。但是我的代码没有正确执行此操作。如果我选择第二个元素,则视图仍将第二个元素显示为选中状态。我知道我的代码有问题。但它是什么?

【问题讨论】:

这不是一个非常用户友好的设计。如果我在组合框中选择某些东西,我希望它是我选择的项目。您应该改为从组合框中删除无效选项。如果选择的有效性基于另一个 UI 元素的选定值,则更改该选择应触发重建组合框的 ItemSource。 这是显示所有项目的要求,不能更改。 我同意不显示无效选项,或者将它们变灰并禁用它们的选择,这就是 UI 的用途。 让您在视图中选择第二个项目,但会显示 IDataErrorInfo 错误。所以你可以从你的设置器中删除你的“验证”并将其放在 IDataErrorInfo 中。但是,如果您将最后一个“好”值设置为您的选定项目并执行 OnPropertyChanged(),它应该可以工作 【参考方案1】:

这是一个非常有趣的问题。首先,我同意其他人的观点,即这是处理无效选择的不推荐方法。正如@blindmeis 建议的那样,IDataErrorInfo 是解决它的好方法之一。

回到问题本身。满足@Faisal Hafeez 想要的解决方案是:

public string SelectedItem

    get  return _selectedItem; 
    set
    
        var oldItem=_selectedItem;
        _selectedItem=value;
        OnPropertyChanged("SelectedItem")

        if (!SomeCondition(value)) //If does not satisfy condition, set item back to old item
            Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => SelectedItem = oldItem),
                                                 DispatcherPriority.ApplicationIdle);
    

Dispatcher 是在另一个 UI 同步期间处理一些 UI 同步的一种优雅方式。例如,在这种情况下,您希望在选择绑定期间重置选择。

这里的一个问题是为什么我们必须首先更新选择。那是因为SelectedItemSelectedValue 是分开分配的,ComboBox 上显示的内容不依赖于SelectedItem(可能是SelectedValue,这里我不确定)。另一个有趣的点是,如果 SelectedValue 发生变化,SelectedItem 必须发生变化,但 SelectedItem 在发生变化时不会更新 SelectedValue。因此,您可以选择绑定到SelectedValue,这样您就不必先分配。

【讨论】:

我们遇到了完全相同的问题,您的解决方法有效。我们还想显示所有项目,尽管不可能选择一个。而且我们不想禁用该控件,因为它只是在很短的时间内无法选择另一个项目。有没有办法编写一个行为来确保选择器 UI 元素始终显示正确的属性值? 对我来说,这会导致最初选择的值略有“闪烁”,但效果很好。 我也非常感谢这个解决方案。似乎正确的答案是让框架将“值”设置为传递,然后将其重置为原始值。但是,有人可以解释为什么 OP 的解决方案不起作用(在通知之前进行测试和分配)。【参考方案2】:

我知道这有点晚了,但从 WPF 4.5 开始,您可以像这样使用延迟命令:

    <ComboBox ItemsSource="Binding ItemsCollection" SelectedItem="Binding SelectedItem, Mode=TwoWay, Delay=1, UpdateSourceTrigger=PropertyChanged" />

这让我在前几天查了几个小时的东西之后救了我。对于其他可能有效或无效的方法,您可以阅读 this 帖子及其 cmets。

【讨论】:

谢谢!!由于这个问题,我搜索了几个小时,延迟解决了,就像我想要的一样! 和卡塔琳娜一样,搜索了几个小时,这很完美,谢谢!!!【参考方案3】:

尝试将 XAML 更改为此

<ComboBox ItemsSource="Binding ItemsCollection" SelectedItem="Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged" />

【讨论】:

"Mode=TwoWay" 是必需的吗?我试图在我的视图模型构造函数中设置默认值,效果很好。但是在 setter 中设置它是失败的。 @FaisalHafeez 是的,因为您也在更改 View 和 ViewModel 中的属性【参考方案4】:
<ComboBox ItemsSource="Binding ItemsCollection" SelectedIndex="Binding currSelection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged " />

在你的虚拟机中


    set 
        currSelection = -1;
    

【讨论】:

【参考方案5】:

如果您正在寻找 MVVM / XAML 可重用解决方案,我将其放在另一个线程中。这使用 WPF 行为,并且非常易于管理。没有外部库。复制到解决方案中。

ComboBox Selected Item Clear Behavior

【讨论】:

请不要发布对其他 Stack Overflow 问题的仅链接答案。相反,投票/标记以关闭为重复,或者,如果问题不是重复的,定制此特定问题的答案。

以上是关于使用 MVVM 重置组合框选定项目的主要内容,如果未能解决你的问题,请参考以下文章

根据数据绑定组合框中的选定项目从访问数据库中删除

如何根据另一个的选定项目过滤一个组合框集合?

用于选定值和搜索功能的 wpf 组合框项目模板

在 PyQt 中,如何将项目和选定项目保存在组合框中

如何根据 DataGridView 中的选定行将组合框中的特定项目设置为选定项?

如何在 sql 语句中保存组合框中的选定项目和文本框中的长数字?