WPF DataGrid - 如何从 ViewModel 中选择位置 [0,0] 的单元格

问题描述 投票:0回答:1

我有一个SelectionMode =“Single”SelectionUnit =“Cell”的DataGrid。 目前,当显示 DataGrid 时,最初没有选择任何单元格。

我需要满足两种情况,要求我了解其中之一:

  1. 单元格值。
  2. 所选单元格的列的所有值。
<DataGrid Margin="0,5,0,0"
          IsReadOnly="True"
          ItemsSource="{Binding DataTableViewModel.DataView}"
          AutoGenerateColumns="true"
          ScrollViewer.CanContentScroll="True"
          CanUserResizeColumns="True" 
          CanUserReorderColumns="True"
          CanUserSortColumns="True"
          CanUserAddRows="false"
          ColumnWidth="auto"
          EnableRowVirtualization="True"
          EnableColumnVirtualization="True"
          VirtualizingPanel.IsVirtualizingWhenGrouping="True"
          VirtualizingPanel.VirtualizationMode="Standard"
          VirtualizingPanel.IsVirtualizing="True"
          SelectionMode="Single"
          SelectionUnit="Cell"
          VerticalAlignment="Top"/>

还有以下附加属性可供绑定:
已选项目
CurrentCell(但是,这是一个结构体,而不是对 DataGridCell 容器的实际引用。)
选定索引

但是,我不知道该使用哪一个,也不知道如何在 ViewModel 中分配它,因为似乎没有一个接受 [0,0] 坐标。

如果我可以确定使用哪个属性来标识所选单元格,我可以包含一些内容,以便在更新所选单元格属性时,我可以确定是否需要单元格值或所有列值,并相应地更新该列表。

我还应该添加,加载控件时不会加载数据,这是视图模型中的一个片段,显示了我如何添加数据:

class DataTableViewModel : PropertyChangedBase
{
    // notify child classes the DataTable has been loaded
    protected event EventHandler<EventArgs> DataTableLoaded;

    public DataView DataView { get { return _dataView; } set { _dataView = value; NotifyPropertyChanged(); } }
    DataView _dataView;

    DataTable _dataTable;

    public DataTable DataTable
    {
        get { return _dataTable; }
        set
        {
            _dataTable = value;
            DataView = value.DefaultView;
            if (DataTableLoaded== null)
                return;
            DataTableLoaded.Invoke(this, new EventArgs());
            NotifyPropertyChanged();

            // Can I put something here to set the selected cell?
        }
    }

所以,我的问题主要是:当 DataGrid 首次显示时,如何将 ViewModel 中的活动单元格位置设置为 [0,0]?

进一步研究:
我发现了一个类似的问题,但答案似乎只处理行而不是单元格: 如何使用 MVVM 应用程序在 WPF 中以编程方式设置 DataGrid 的选定项?

wpf mvvm data-binding datagrid
1个回答
0
投票

这是一项相当困难的任务,特别是考虑到MVVM模式的实现。您必须在 View 和 ViewModel 之间提供某种双向通信才能选择单元格。

执行此操作的一种方法是:

using Simplified; // Space with my implementations of ViewModelBase and RelayCommand.
using System.Data;

namespace Core2023.SO.Jase99.Question77673006
{
    public class CellInfoData : ViewModelBase
    {
        public bool IsSelected { get => Get<bool>(); set => Set(value); }
        public int Row { get; }
        public int Column { get; }

        public CellInfoData(int row, int column)
        {
            Row = row;
            Column = column;
        }
    }
    public class CellContentInfoData<T> : CellInfoData
    {
        public T Content { get => Get<T>(); set => Set(value); }
        public CellContentInfoData(int row, int column) : base(row, column) { }
    }

    public class CellIntInfoData : CellContentInfoData<int>
    {
        public CellIntInfoData(int row, int column) : base(row, column) { }
    }
    public class CellStringInfoData : CellContentInfoData<string>
    {
        public CellStringInfoData(int row, int column) : base(row, column) { }
    }

    public class SomeViewModel : ViewModelBase
    {
        public DataTable Table { get => Get<DataTable>(); private set => Set(value); }

        public CellInfoData? LastSelectedCell { get => Get<CellInfoData>(); private set => Set(value); }

        public SomeViewModel()
        {
            DataTable table = new ();
            table.Columns.Add(new DataColumn("Number", typeof(CellIntInfoData)));
            table.Columns.Add(new DataColumn("Text", typeof(CellStringInfoData)));


            foreach (var item in new (int, string)[] { (1, "First"), (2, "Second"), (3, "Third" ) })
            {
                DataRow row = table.NewRow();
                CellInfoData cell = new CellIntInfoData(table.Rows.Count, 0) { Content = item.Item1 };
                cell.PropertyChanged += CellPropertyChanged;
                row[0] = cell;
                cell = new CellStringInfoData(table.Rows.Count, 1) { Content = item.Item2 };
                cell.PropertyChanged += CellPropertyChanged;
                row[1] = cell;
                table.Rows.Add(row);
            }

            Table = table;
        }

        private void CellPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (sender is CellInfoData cell && e.PropertyName == nameof(CellInfoData.IsSelected))
            {
                if (cell.IsSelected)
                {
                    LastSelectedCell = cell;
                }
            }
        }

        public RelayCommand SelectedCell00 => GetCommand(() =>
        {
            ((CellInfoData)Table.Rows[0][0]).IsSelected = true;
        });
    }
}
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

namespace Core2023.SO.Jase99.Question77673006
{
    public partial class SelectedCellWindow : Window
    {
        public SelectedCellWindow()
        {
            InitializeComponent();
        }

    }

    public static class DataGridCellHelper
    {
        const string stringTemplate =
@"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
              xmlns:local=""clr-namespace:Core2023.SO.Jase99.Question77673006;assembly=Core2023"">
    <ContentControl Content=""{{Binding {0}}}""
                    local:DependencyObjectHelper.First=""{{Binding IsSelected, RelativeSource={{RelativeSource AncestorType=DataGridCell}}}}""
                    local:DependencyObjectHelper.Second=""{{Binding Content.IsSelected, RelativeSource={{RelativeSource Self}}}}""/>
</DataTemplate>
";
        //local:DependencyObjectHelper.Second=""{{Binding Content.IsSelected, RelativeSource={{RelativeSource Self}}}}""
        public static readonly EventHandler<DataGridAutoGeneratingColumnEventArgs> OnGeneratingColumn = (sender, e) =>
        {
            e.Column = new DataGridTemplateColumn()
            {
                Header = e.Column.Header,
                CellTemplate = (DataTemplate)XamlReader.Parse(string.Format(stringTemplate, e.PropertyName))
            };
        };
    }

    public static class DependencyObjectHelper
    {
        public static object GetFirst(DependencyObject obj)
        {
            return obj.GetValue(FirstProperty);
        }

        public static void SetFirst(DependencyObject obj, object value)
        {
            obj.SetValue(FirstProperty, value);
        }

        // Using a DependencyProperty as the backing store for First.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FirstProperty =
            DependencyProperty.RegisterAttached(
                "First",
                typeof(bool?),
                typeof(DependencyObjectHelper),
                new FrameworkPropertyMetadata(null)
                {
                    PropertyChangedCallback = (d,e) => d.SetValue(SecondProperty, e.NewValue),
                    BindsTwoWayByDefault = true
                });
        public static object GetSecond(DependencyObject obj)
        {
            return obj.GetValue(SecondProperty);
        }

        public static void SetSecond(DependencyObject obj, object value)
        {
            obj.SetValue(SecondProperty, value);
        }

        // Using a DependencyProperty as the backing store for Second.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SecondProperty =
            DependencyProperty.RegisterAttached(
                "Second",
                typeof(bool?),
                typeof(DependencyObjectHelper),
                new FrameworkPropertyMetadata((bool?) null)
                {
                    PropertyChangedCallback = (d, e) => d.SetValue(FirstProperty, e.NewValue),
                    BindsTwoWayByDefault = true
                });

    }
}
<Window x:Class="Core2023.SO.Jase99.Question77673006.SelectedCellWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Core2023.SO.Jase99.Question77673006"
        xmlns:converters="clr-namespace:Converters;assembly=CommonCore"
        mc:Ignorable="d"
        Title="SelectedCellWindow" Height="450" Width="800"
        DataContext="{DynamicResource vm}">
    <Window.Resources>
        <local:SomeViewModel x:Key="vm"/>
        <!--<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                      xmlns:local="clr-namespace:Core2023.SO.Jase99.Question77673006"
                      x:Key="contentControl">
            <ContentControl Content="{Binding }"
                            local:DependencyObjectHelper.First="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridCell}}"
                            local:DependencyObjectHelper.Second="{Binding Content.IsSelected, RelativeSource={RelativeSource Self}}"/>
        </DataTemplate>-->
        <DataTemplate DataType="{x:Type local:CellIntInfoData}">
            <TextBlock Text="{Binding Content, StringFormat='Number: 0'}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:CellStringInfoData}">
            <TextBlock>
                <Run Text="Text:"/>
                <Run Text="{Binding Content}"/>
            </TextBlock>
        </DataTemplate>
    </Window.Resources>
    <UniformGrid Rows="1">
        <DataGrid ItemsSource="{Binding Table}"
                  CanUserAddRows="False"
                  AutoGeneratingColumn="{x:Static local:DataGridCellHelper.OnGeneratingColumn}"
                  AutoGenerateColumns="True"
                  SelectionMode="Single"
                  SelectionUnit="Cell">
        </DataGrid>
        <UniformGrid Columns="1">
            <TextBlock>
                <Run Text="{Binding LastSelectedCell.Row, Mode=OneWay}"/>
                <LineBreak/>
                <Run Text="{Binding LastSelectedCell.Column, Mode=OneWay}"/>
            </TextBlock>
            <Button Content="Selected Cell 0,0"
                    Command="{Binding SelectedCell00}"/>
        </UniformGrid>
    </UniformGrid>
</Window>
© www.soinside.com 2019 - 2024. All rights reserved.