2

循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(10) -- 在Dat...

 9 months ago
source link: https://www.cnblogs.com/wuhuacong/p/17774455.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(10) -- 在DataGrid上直接编辑保存数据

有时候,一些数据的录入可能需要使用表格直接录入会显得更加方便快捷,这种情况有时候也是由于客户使用习惯而提出,本篇随笔介绍在WPF应用端上使用DataGrid来直接新增、编辑、保存数据的处理。

录入数据的时候,我们都采用在一个窗体界面中,根据不同内容进行录入,但是有时候涉及主从表的数据录入,从表的数据有时候为了录入方便,也会通过表格控件直接录入。在Winform开发的时候,我们很多时候可以利用表格GridControl控件来直接录入数据;在BS的Vue&Elment前端项目中,也可以利用第三方组件vxe-table直接录入表格数据。

在不同的前端处理中,对于数据直接录入的处理,我写了一些随笔,可以参考。

在Winform界面中,也可以实现基于表格数据的直接录入,如下随笔所示《在DevExpress程序中使用Winform分页控件直接录入数据并保存》、《在DevExpress程序中使用GridView直接录入数据的时候,增加列表选择的功能》、《在Winform中直接录入表格数据和在Vue&Elment中直接录入表格数据的比较》。

如在Vue的前端录入中,也可以实现数据直接录入的,详细可以参考随笔介绍《在Vue前端界面中,几种数据表格的展示处理,以及表格编辑录入处理操作》。

在Bootstrap的Web开发中,也可以使用控件实现数据表格的直接录入处理,如随笔介绍《在Bootstrap开发框架中使用dataTable直接录入表格行数据》。

1、在DataGrid上直接编辑保存数据的界面效果

一般情况下,我们可能会利用新的窗口来承载数据表格的内容,这样展现的方式会比较灵活,也比较丰富一些,如下界面所示。

8867-20231019124004513-1070902588.png

有时候,我们也会采用直接录入数据的方式,来快速直接录入一些简单的数据,如下界面所示。

8867-20231019124056198-1637022182.png

 对于新增或者编辑,单击某行记录的时候,会进行编辑处理,如下界面所示。

8867-20231019124215186-1794319437.png

但是,往往很多数据不是简单的录入,我们可能会涉及一些下拉列表,以及一些自定义的处理,如下所示。

8867-20231019124334976-1127344015.png

普通下拉列表的处理

8867-20231019124416776-1518803901.png

也可能是一些特殊的自定义处理,如选择图标的操作。

8867-20231019124657166-1378630645.png

 当然还有一些单选框、复选框等处理,那些也是类似处理,不在赘述。

2、实现过程和思路

 大致了解了一些常规的直接编辑处理,我们来看看如何具体实现上面的效果的。

一般默认的DataGrid就是可以编辑内容的,或者你把只读属性IsReadOnly设置为false即可,如下: IsReadOnly="False"

编辑情况下,我们列表就不用设置复选框来勾选,而采用序号显示的方式,如下设置代码。

<DataGrid
    x:Name="grid"
    hc:DataGridAttach.ShowRowNumber="True"
    AddingNewItem="grid_AddingNewItem"
    AutoGenerateColumns="False"
    CanUserAddRows="True"
    CanUserSortColumns="False"
    GridLinesVisibility="Vertical"
    HeadersVisibility="All"
    ItemsSource="{Binding ViewModel.MenuItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    RowHeaderWidth="60"
    SelectionMode="Extended"
    VerticalGridLinesBrush="AliceBlue">
    <!--<DataGrid.RowHeaderTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridRow}}" />
        </DataTemplate>
    </DataGrid.RowHeaderTemplate>-->

对于直接编辑的数据列表,需要采用ObservableCollection<T>的集合处理,如果采用List<T>是不可以正常处理的。

        /// <summary>
        /// 编辑的数据列表
        /// </summary>
        [ObservableProperty]
        private ObservableCollection<MenuInfo>? menuItems;

定义好集合后,我们对其中任何记录的处理,都是可以反映到界面上的了。如果需要新增记录,我们添加一个新的记录到上面的集合中即可体现到界面上了。

例如我们通过按钮新增一条记录,触发一个命令处理即可。

<Button
    Width="80"
    Height="40"
    Margin="5"
    hc:IconElement.Geometry="{StaticResource AddGeometry}"
    Command="{Binding AddNewCommand}"
    Content="新增"
    Style="{StaticResource ButtonInfo}" />

其中AddNewCommand的命令操作,就是往上面的集合中添加一个记录即可体现到界面上了。

/// <summary>
/// 新增记录
/// </summary>
[RelayCommand]
private void AddNew()
{
    this.ViewModel!.MenuItems!.Add(new MenuInfo()
    {
        SystemType_ID = App.ViewModel.SystemType,
        //初始化
    });
}

界面效果如下所示。

8867-20231019131318622-1225096122.png

 一般的列,编辑状态下就是文本框的处理,如下代码所示。

<DataGrid.Columns>
    <DataGridTextColumn
        MinWidth="50"
        Binding="{Binding Name}"
        Header="显示名称" />
8867-20231019125403605-2029032824.png

而如果要自定义常规显示和编辑状态下的不同,那么就需要自定义模板<DataGridTemplateColumn.CellTemplate>、<DataGridTemplateColumn.CellEditingTemplate>来处理,如下所示。

例如对于图标列,两个模板内容不同的。

<DataGridTemplateColumn Width="80" Header="图标">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ui:SymbolIcon
                Width="32"
                FontSize="32"
                Symbol="{Binding Icon}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <StackPanel
                MinWidth="200"
                Background="{DynamicResource RegionBrush}"
                Orientation="Horizontal">
                <ui:SymbolIcon
                    FontSize="24"
                    Foreground="red"
                    Symbol="{Binding Icon, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                <Button
                    Command="{Binding Path=DataContext.SelectIconCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
                    CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}, Path=SelectedItem}"
                    Content="选择图标"
                    Foreground="red"
                    Style="{StaticResource ButtonInfo.Small}" />
            </StackPanel>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

编辑模板下的显示和常规的不一样,以便提供一些入口我们进行相应的处理。

8867-20231019125705667-1177574845.png

这里通过按钮,触发一个选择图标的命令操作,如下代码所示。

/// <summary>
/// 选择图标
/// </summary>
[RelayCommand]
private void SelectIcon(MenuInfo info)
{
    //基于 SymbolRegular 图标选择
    var page = App.GetService<SymbolRegularSelectPage>();
    page!.ViewModel.ResetData();//每次更新数据
    if (page.ShowDialog() == true)
    {
        var item = page.ViewModel.SelectedItem;
        info.Icon = item.Text;
    }
}

而对于一些普通的下拉列表,我们通过提供数据源的方式来绑定列表即可。

8867-20231019130021213-1118212908.png

 根据实际情况初始化下拉列表的内容,如下所示。

/// <summary>
/// 初始化字典
/// </summary>
/// <returns></returns>
public async Task InitDictItem()
{
    //初始化性别的列表
    this.GenderItems = new List<CListItem>()
    {
        new("男"),
        new("女")
    };
    this.IsExpandToYesNoItems = new List<CListItem>()
    {
        new() { Text="是", Value = "1"},
        new() { Text="否", Value = "0"},
    };
    this.IsVisibleToYesNoItems = new List<CListItem>()
    {
        new() { Text="是", Value = "1"},
        new() { Text="否", Value = "0"},
    };
}

其中XAML中的下拉列表的处理代码如下所示。

<DataGridTemplateColumn Width="100" Header="是否可见">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Foreground="{Binding Visible, Converter={StaticResource NumberToColorReConverter}}" Text="{Binding Visible, Converter={StaticResource NumberToYesNoStrConverter}}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox
                ItemsSource="{Binding DataContext.ViewModel.IsVisibleToYesNoItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
                SelectedValue="{Binding Visible, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                SelectedValuePath="Value"
                Text="{Binding Visible, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource NumberToYesNoStrConverter}}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

注意上面的绑定和选择路径,从而可以通过修改直接反映到集合上。

其中获取记录集合的处理操作代码如下所示。

public override async Task GetData()
{       
    //转换下分页信息
    ConvertPagingInfo();

    var result =  await service.GetListAsync(this.PageDto);
    if (result != null)
    {
        //this.MenuItems.Clear();
        this.MenuItems = new ObservableCollection<MenuInfo>(result.Items?.ToList());
        this.PagerInfo.RecordCount = result.TotalCount;
    }
}

通过获得查询数据的处理,我们可以显示具体的页面信息。

8867-20231019130438741-316847646.png

<StackPanel Orientation="Horizontal">
    <hc:Pagination
        Margin="0,10,0,10"
        DataCountPerPage="{Binding ViewModel.PagerInfo.PageSize}"
        IsJumpEnabled="True"
        MaxPageCount="{Binding ViewModel.PagerInfo.MaxPageCount}"
        MaxPageInterval="5"
        PageIndex="{Binding ViewModel.PagerInfo.CurrentPageIndex}">
        <hc:Interaction.Triggers>
            <hc:EventTrigger EventName="PageUpdated">
                <hc:EventToCommand Command="{Binding ViewModel.PageUpdatedCommand}" PassEventArgsToCommand="True" />
            </hc:EventTrigger>
        </hc:Interaction.Triggers>
    </hc:Pagination>

    <TextBlock
        Margin="20,0,0,0"
        VerticalAlignment="Center"
        Text="共有记录数" />
    <TextBlock
        Margin="10,0,0,0"
        VerticalAlignment="Center"
        Foreground="Blue"
        Text="{Binding ViewModel.MenuItems.Count}" />
    <TextBlock
        Margin="10,0,0,0"
        VerticalAlignment="Center"
        Text="/" />
    <TextBlock
        Margin="10,0,0,0"
        VerticalAlignment="Center"
        Foreground="Blue"
        Text="{Binding ViewModel.PagerInfo.RecordCount}" />
    <TextBlock
        Margin="10,0,0,0"
        VerticalAlignment="Center"
        Text="条" />
</StackPanel>

而对于父级记录的处理,我们可以在里面增加一个下列树状列表的处理即可。

<DataGridTemplateColumn Width="150" Header="上级菜单">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Foreground="{Binding PID}" Text="{Binding PID, Converter={StaticResource MenuIdToNameConverter}}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <control:MenuControl Text="" Value="{Binding PID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

这样就可以弹出下列列表的信息了,这样我们可以绑定各种不同的树状数据集合,从而达到更好的编辑处理体验了。

8867-20231019130747335-641507934.png

数据记录的删除操作,我们可以判断集合对象的Id是否为空,来判断是否是新增的,然后采用不同的方式删除处理。

        /// <summary>
        /// 删除操作
        /// </summary>
        /// <param name="info">业务对象</param>
        [RelayCommand]
        private async Task Delete(MenuInfo info)
        {
            if (info == null) return;
            if (info.Id.IsNullOrEmpty())
            {
                this.ViewModel.MenuItems!.Remove(info);
            }
            else
            {
                if (MessageDxUtil.ShowYesNoAndWarning("您确认删除该记录?") != System.Windows.MessageBoxResult.Yes)
                    return;
                this.ViewModel.DeleteCommand.Execute(info);
            }
        }

而保存的时候,我们对记录进行新增或者修改即可,统一处理。

        /// <summary>
        /// 用于子类重写的保存更新操作
        /// </summary>
        /// <returns></returns>
        protected virtual async Task<bool> InsertOrUpdate()
        {
            bool result = false;
            if (this.MenuItems != null)
            {
                foreach (var item in this.MenuItems)
                {
                    await service.InsertOrUpdateAsync(item, item.Id);
                }
                result = true;
            }
            return result;
        }

以上就是我们在数据编辑的处理思路和常规的做法,如果您对界面有更高的要求,也可以和我一起讨论处理。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK