【问题标题】:DataGrid not showing focus for SelectedItem when ViewModel is redisplayed重新显示 ViewModel 时,DataGrid 未显示 SelectedItem 的焦点
【发布时间】:2013-12-06 13:53:34
【问题描述】:

PersonsViewModel 有一个对应的 DataTemplate 和一个绑定到 PersonList 的 DataGrid。 DataGrid.SelectedItem 具有到SelectedPerson 的双向数据绑定,因此当我在视图中选择一行时,PersonList 中的相应项被分配给SelectedPerson

它工作正常,除了一个问题:当我切换屏幕时,比如说,到PersonDetailScreen,然后回来,选择焦点丢失了!我的意思是,SelectedPerson 仍然包含它以前的值,但是 DataGrid 出来时在视觉上没有被选中。

我做了一个测试,在视图模型中创建了一个双向数据绑定“SelectedIndex”。然后,当它返回时,我可以看到实际的选择仍然存在于 viewmodel 中,问题似乎是:

“当加载 ViewModel 的数据模板并且此类视图模型中的某些“SelectedIndex”数据绑定属性已经包含值时,如何聚焦 ItemsControlSelectedIndex?”

【问题讨论】:

    标签: wpf data-binding mvvm datagrid focus


    【解决方案1】:

    如果您有TwoWay Binding,那么您可以从您的视图模型中设置DataGrid.SelectedItem 值。不过,我对您的设置有点困惑……当您说“当我切换屏幕时……然后回来时,选择焦点丢失了”,这听起来有点像你保持视图模型活着,但不是视图?我更习惯于每次都显示一个新视图,但修复方法都是一样的。

    现在,如果这是我的观点之一,我会将数据从构造函数加载到任何集合中,并(在基类内部)将绑定到 ListBox.SelectedItem 属性的数据的 CurrentItem 属性设置为集合中的第一项。您也应该这样做,只是不要将其设置为第一项,而是将其设置为最后选择的任何一项。

    所以答案就是在每次显示视图时设置SelectedItem 属性。但是,如果您说 SelectedItem 属性已设置为应选择的项目,则可能需要先将其设置为任何其他值,然后再将其设置回正确的项目。

    【讨论】:

    • 嗨,我所做的实际上是切换视图模型。我什至没有视图,数据网格位于 DataTemplate 中,其 DataType 是给定的视图模型。因此,当我使用所述视图模型设置“WindowContent”属性时,视图会自动“弹出”,每次都是新视图。就我而言,这是一种非常有意的 ViewModel-First 方法。
    • 你的最后一段很有趣......在后面的代码中,我可以强制一些事情发生(在我看来,WPF 缺乏一些琐碎的行为......),但这需要特定的重构:精简我的数据模板,只包含对更厚的、代码隐藏填充的用户控件的引用。
    • 这不一定是真的……您可以使用 MVVM 并将您的代码留在代码后面。尽管如此,我仍然建议您为每个视图模型使用一个UserControl(查看您的DataTemplate 的内容)......您仍然可以保持您的视图模型优先方法,但它会更容易维护视图文件而不是一堆DataTemplates。
    • 我相信我们在这里同意:数据模板是通过其 DataType 拥有 ViewModelFirst 的好方法。但是在数据模板中,我们可以立即放置我们普通的旧视图(以 UserControl 子类的形式,后面有可变数量的代码)。我在很多教程和 SO 示例中都看到过这种模式......
    【解决方案2】:

    我们在这里谈论的是什么,项目选择或项目焦点?

    如果您希望在呈现模板时让视觉项目获得焦点,最好的办法是在您的 xaml 代码中手动设置焦点,例如,将处理程序挂钩到页面的“已加载”事件并设置通过调用其“Focus()”方法手动聚焦到您的数据网格。

    我知道这违反了一些 MVVM 规则,但焦点管理高度依赖于页面的可视化树,因此无法通过应尽可能独立于视图的视图模型正确建模。

    【讨论】:

    • 感谢您的关注。 ViewModel 保持选择了哪个元素的逻辑状态,并且可以正常工作。选择本身有效,因为如果我调试(断点)所选项目和所选索引仍设置为正确的值。问题是:DataGrid 的 VISUAL 状态就像选择了任何东西(每一行都是相等的,具有“未选择”状态。不是控件没有焦点,而是应该显示为选中的行具有相同的外观与其他行一样,即使数据网格本身具有焦点。
    • 快速提问。当您离开 Persons 视图转到另一个屏幕,然后返回到该屏幕时,PersonsViewModel 是与以前相同的实例还是您再次创建它?
    • 相关问题。如果 PersonsViewModel 是同一个实例,您是否可能重新填充您的 PersonList?可能发生的情况是 SelectedPerson 是原始 PersonList 中的一个项目。如果您在任何时候使用 Person 的新实例重新填充 PersonList,那么即使 SelectedPerson 仍然拥有一个 Person 实例,此实例不再在 PersonList 中,并且您的数据网格无法在其 ItemSource 中找到所选人员。
    • 实例是一样的。它实际上是一个 parentViewModel 的属性,它是 MainWindow 的 DataContext。 MainWindow 和 parentViewModel 在程序运行时只有一个实例,我将 parentViewModel.CurrentScreen 与其长期存在的 ScreenViewModel 实例切换。发生的情况是 View 中的 DataGrid 每次都重新创建,具有一个“SelectedIndex”,具有双向数据绑定到 ViewModel 中的相应属性...
    【解决方案3】:

    感谢 Sheridan 的洞察力,我比我想象的更容易解决问题,而且有点无意。

    我在 DataTemplate 中直接定义了 DataGrid。加载模板后,我相信,尽管我完全不确定,将“SelectedItem”或“SelectedItem”值传递给视图所需的一些初始化步骤“丢失”了。

    我计划在后面的代码中手动重新选择,所以我所做的就是将数据网格移动到我从头开始创建的一些 UserControl。但是,DataTemplate 现在在其自身内部实例化了一个适当的视图(从 UserControl 派生),该视图又包含数据网格,这一事实似乎“使更多通知工作”,可以这么说,并且视图像它一样显示选定的行总是应该的。

    所以,现在我的 DataTemplate 中有这个:

    <DataTemplate x:Name="PersonScreenTemplate" DataType="{x:Type vm:PersonScreenViewModel}">   
        <vw:PersonScreenViewView/>
    </DataTemplate>
    

    这似乎是 ViewModel-first 的完美纯 WPF 设计模式:一个数据模板,其内容是单个用户控件,它依次声明、绑定和处理所有内容。

    【讨论】:

      猜你喜欢
      • 2014-11-11
      • 2014-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多