WPF数据表格:单击时折叠详细行

5
我需要在用户点击时折叠WPF DataGrid的详细行,并在再次单击时重新显示它。同时,我还想保留DataGridRoDetailsVisibilityMode为VisibleWhenSelected,使用单选。
基于别处发布的帖子,我想到了这个解决方案:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/0a45b3a7-46d0-45a9-84b2-0062f07f6fec#eadc8f65-fcc6-41df-9ab9-8d93993e114c
    private bool _rowSelectionChanged;

    private void dgCompletedJobs_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        _rowSelectionChanged = true;
    }

    private void dgCompletedJobsMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        DependencyObject dep = (DependencyObject)e.OriginalSource;

        //navigate up the tree
        while (dep != null &&
            !(dep is DataGridCell) &&
            !(dep is DataGridColumnHeader))
        {
            dep = VisualTreeHelper.GetParent(dep);
        }

        if (dep == null)
        {
            return;
        }

        DataGridCell dgc = dep as DataGridCell;
        if (dgc != null)
        {
            //navigate further up the tree
            while (dep != null && !(dep is DataGridRow))
            {
                dep = VisualTreeHelper.GetParent(dep);
            }

            DataGridRow dgr = dep as DataGridRow;
            DataGrid dg = sender as DataGrid;
            if (dg != null && dgr != null)
            {
                if (dgr.IsSelected && !_rowSelectionChanged)
                {
                    dg.RowDetailsVisibilityMode =
                        (dg.RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.VisibleWhenSelected)
                            ? DataGridRowDetailsVisibilityMode.Collapsed
                            : DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
                }
                else
                {
                    dg.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
                }
            }
        }
        _rowSelectionChanged = false;
    }

这似乎很好地解决了我的问题,但我有一种不安的感觉,认为这可以更简单和优雅地完成,特别是因为我在此项目中使用了MVVM。但是,我认为这是事件驱动代码后台的可接受用法,因为它纯粹是表示逻辑。
有没有更简洁的解决方案?
4个回答

4
要使用“正确”的MVVM方法实现这一点,您应该将RowDetailsVisibilityMode绑定到视图模型上的属性:
<DataGrid x:Name="dgCompletedJobs" RowDetailsVisibilityMode="{Binding RowDetailsVisible}"/>

你的视图模型属性可能是这样的:

private DataGridRowDetailsVisibilityMode _rowDetailsVisible;
public DataGridRowDetailsVisibilityMode RowDetailsVisible
{
    get { return _rowDetailsVisible; }
    set {
        _rowDetailsVisible = value;
        if (PropertyChanged != null) {
             PropertyChanged(this, new PropertyChangedEventArgs("RowDetailsVisible"));
        }
    }
}

为了将鼠标点击事件链接到属性的更改,您可以使用一些花哨的附加行为命令,如此处所示,或者直接使用代码后台调用视图模型(对于简单任务,我经常这样做)。
private void dgCompletedJobsMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    Window1ViewModel viewModel = (Window1ViewModel)DataContext;
    if (viewModel.RowDetailsVisible == DataGridRowDetailsVisibilityMode.Collapsed) {
        viewModel.RowDetailsVisible = DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
    } else {
        viewModel.RowDetailsVisible = DataGridRowDetailsVisibilityMode.Collapsed;
    }
}

1
运行完美。谢谢。 - GeoffCoope
在一个真正纯粹的MVVM中,如果你想在WPF之外重用VM,那么VM不应该依赖于System.Windows.Controls,其中包括DataGridRowDetailsVisibilityMode - Jinjinov
@Jinjinov 是的,在Viewmodel上创建一个布尔或枚举属性,并使用转换器。留给读者作为练习。 - J S
这真的很糟糕。正确的MVVM不允许将纯粹的视图状态移动到视图模型中。相反,您可以使用触发器、依赖属性、附加行为、路由事件等来处理这种情况。这是纯粹的UI逻辑业务。例如,不要在视图模型中创建这样的属性,而是在视图中创建一个依赖属性并将其绑定。 - undefined

1

为什么不使用sender参数?如果事件在DataGrid上定义,那么sender始终是DataGrid!使用安全转换并检查null以确保安全,但这应该就可以解决问题了。

代码似乎过于复杂,因为你正在通过可视树从原始源返回到你的DataGrid。

        private void dataGridMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        DataGrid dg = sender as DataGrid;
        if (dg == null)
            return;
        if (dg.RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.VisibleWhenSelected)
            dg.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed;
        else
            dg.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
    }

当您选择另一行时,此功能无法按预期工作 - 新选择的行具有取决于先前行中详细信息状态的可见详细信息。 - Jinjinov

1

这个方法结合了Grafix和Prethen的答案。

如果你只想在行已经被选中时切换行细节,可以使用它:

private void DataGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (sender is DataGrid dataGrid && 
        e.OriginalSource is FrameworkElement frameworkElement && 
        frameworkElement.DataContext == dataGrid.SelectedItem)
    {
        if (dataGrid.RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.VisibleWhenSelected)
            dataGrid.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed;
        else
            dataGrid.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
    }
}

1

我想出了一种不同的方法,但不是“正确”的MVVM方式,因为它使用了代码后台(与上面提供的答案中的某些代码一样),但只需几行代码就可以完成。

通过针对PreviewMouseUp事件进行编码,我能够获得所需的确切行为。该代码确保您实际上已单击了网格中的某个内容,并且要折叠它,必须是已打开的同一行。

 private void UIElement_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        DataGrid grid = sender as DataGrid;

        if (grid != null)
        {
            FrameworkElement element = e.OriginalSource as FrameworkElement;

            if (element?.DataContext is MyCustomObject)
            {
                if (grid.SelectedItem == (MyCustomObject) ((FrameworkElement) e.OriginalSource).DataContext)
                {
                    grid.SelectedIndex = -1;
                    e.Handled = true;
                }
            }
        }
    }

MVVM和代码后台不是互斥的。MVVM设计模式并不关心编译器概念,例如部分类。对设计模式的错误理解的传播令人担忧。将代码后台与MVVM连接在一起是错误的。在某些领域,Stackoverflow引发了严重的问题。过多传播错误信息和不良实践。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接