Wpf 使用 Prism 实战开发Day19

待办事项功能页面完善以及优化

概要:

由于待办事项功能页,数据已正常渲染出来了。但页面新增,查询,修改,删除等功能还未实现。本章节来实现页面的请求后台实现CURD(增删改查)


一.待办事项查询搜索删除增加等功能

根据渲染出来的待办事项,点击对应的待办事项时,查找出该条数据,显展示在编辑窗口中。

同时在搜索框中输入的参数或选择的待办事项状态,按下Enter按键时,触发查询。

1.首先把待办事项页修改成支持编辑的功能,也就是增加触发器。需要引入微软行为类 behaviors

ToDoView.xaml 前端页引入命名空间

xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

 修改 Grid,支持编辑功能

<!--自定义内容区域-->
<Grid Width="220" MinHeight="180" MaxHeight="250" Margin="8" >
    <!--行为触发器-->
    <i:Interaction.Triggers>
             <!--鼠标左击事件-->
            <i:EventTrigger EventName="MouseLeftButtonUp">
                <!--设置命令-->
                <i:InvokeCommandAction 
                    CommandParameter="{Binding}"
                    Command="{Binding DataContext.SelectedCommand ,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}}"/>
            </i:EventTrigger>
    </i:Interaction.Triggers>
</Grid>
  1. EventTrigger 通过EventName 锁定当前事件名称
  2. RelativeSource 指定绑定的属性
  3. Mode 设置查找模式
  4. AncestorType 设置绑定的类型
  5. DataContext 设置绑定命令
  6. CommandParameter 设置当前绑定命令传递到后台的参数

2.待办事项对应的后台处理逻辑类 (ToDoViewModel)

namespace MyToDo.ViewModels
{
   public class ToDoViewModel: NavigationViewModel
    {
        public ToDoViewModel(IToDoService toDoService, IContainerProvider provider):base(provider)
        {
            ToDoDtos = new ObservableCollection<ToDoDto>();
            SelectedCommand = new DelegateCommand<ToDoDto>(Selected);
            this.toDoService = toDoService;
        }

        private bool isRightDrawerOpen;
        /// <summary>
        /// 右侧编辑窗口是否展开
        /// </summary>
        public bool IsRightDrawerOpen
        {
            get { return isRightDrawerOpen; }
            set { isRightDrawerOpen = value; RaisePropertyChanged(); }
        }


        public DelegateCommand<ToDoDto> SelectedCommand { get; private set; }
        private readonly IToDoService toDoService;


        private ToDoDto currentDto;
        /// <summary>
        /// 编辑选中/新增对象
        /// </summary>
        public ToDoDto CurrentDto
        {
            get { return currentDto; }
            set { currentDto = value; RaisePropertyChanged(); }
        }
        private async void Selected(ToDoDto obj)
        {
            try
            {
                UpdateLoading(true);
                //进行数据查询
                var todoResult = await toDoService.GetFirstOfDefaultAsync(obj.Id);
                if (todoResult.Status)
                {
                    //把拿到的结果,赋给一个当前选中的ToDoDto
                    CurrentDto = todoResult.Result;
                    IsRightDrawerOpen = true;//打开窗口
                }
            }
            catch (Exception ex)
            {
                await Console.Out.WriteLineAsync(ex.Message);
            }
            finally
            {
                UpdateLoading(false);
            }
          
        }
    }
}

数据查出来后,前端也需要绑定对应的数据模型,才能显示


3.搜索框输入文本,按下Enter 按键时触发查询

首先,需要在搜索框绑定一个属性,用来接收用户输入的参数。那么对应后台逻辑处理类  ToDoViewModel 需定义这么一个属性。然后前台需要绑定该属性。并且设置绑定的模式Mode为(双向绑定)TwoWay,并且设置(更新的数据源)UpdateSourceTrigger为PropertyChanged(一旦发生变化,马上通知更新),

<TextBox Text="{Binding Search,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />

接着,给输入框绑定一个回车(Enter)事件。同时绑定一个(ExecuteCommand)指令,以及(CommandParameter)传递给后台的处理的参数。

<!--设置绑定模式和更新数据源类型-->
<TextBox Text="{Binding Search,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}">
    <!--搜索框绑定回车事件-->
    <TextBox.InputBindings>
        <!--通过Key 绑定-->
        <KeyBinding Key="Enter"  Command="{Binding ExecuteCommand}" CommandParameter="查询"/>
    </TextBox.InputBindings>
</TextBox>

4.处理逻辑类 (ToDoViewModel)添加一个通用(ExecuteCommand)绑定指令,根据(CommandParameter)传递不同的参数处理不同的逻辑。例如:

namespace MyToDo.ViewModels
{
   public class ToDoViewModel: NavigationViewModel
    {
        public ToDoViewModel(IContainerProvider provider):base(provider)
        {
            ExecuteCommand = new DelegateCommand<string>(Execute);
        }
        public DelegateCommand<string> ExecuteCommand{ get; private set; }
        private string search;
        /// <summary>
        /// 用户输入的搜索条件
        /// </summary>
        public string Search
        {
            get { return search; }
            set { search = value; }
        }
        /// <summary>
        /// 根据不同的参数,处理不同的逻辑
        /// </summary>
        /// <param name="obj"></param>
        private void Execute(string obj)
        {
            switch (obj)
            {
                case "新增":
                    Add();
                    break;
                case "查询":
                    GetDataAsync();
                    break;
                case "保存":
                    Save();
                    break;
            }
        }
    }
}

5.待办事项功能页,根据待办不同的状态,显示不同的颜色

在ToDoView.xaml 渲染背景颜色Border 中,增加一个触发器来处理,例如:

 <!--整个框圆角-->
     <Border CornerRadius="3" Grid.RowSpan="2" >
         <!--增加触发器,根据绑定的状态不同,显示不同的颜色-->
         <Border.Style>
             <Style TargetType="Border">
                 <Style.Triggers>
                     <DataTrigger Binding="{Binding Status}" Value="0">
                         <Setter Property="Background" Value="#3CB371"/>
                     </DataTrigger>
                     <DataTrigger Binding="{Binding Status}" Value="1">
                         <Setter Property="Background" Value="#1E90EF"/>
                     </DataTrigger>
                 </Style.Triggers>
             </Style>
         </Border.Style>
     </Border>

 6.右键删除功能

ToDoView.xaml 前端页面修改,增加绑定指定

 <!--右上角按钮-->
 <md:PopupBox HorizontalAlignment="Right" Panel.ZIndex="1">
         <Button Content="删除"  CommandParameter="{Binding}"
          Command="{Binding DataContext.DeleteCommand ,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}}"/>
 </md:PopupBox>

后台ToDoViewModel 逻辑实现

namespace MyToDo.ViewModels
{
   public class ToDoViewModel: NavigationViewModel
    {
        public ToDoViewModel(IToDoService toDoService, IContainerProvider provider):base(provider)
        {
            ToDoDtos = new ObservableCollection<ToDoDto>();
            DeleteCommand = new DelegateCommand<ToDoDto>(Delete);
            this.toDoService = toDoService;
        }
        public DelegateCommand<ToDoDto> DeleteCommand { get; private set; }
        private ObservableCollection<ToDoDto> toDoDtos;
        private readonly IToDoService toDoService;

        /// <summary>
        /// 创建数据的动态集合
        /// </summary>
        public ObservableCollection<ToDoDto> ToDoDtos
        {
            get { return toDoDtos; }
            set { toDoDtos = value;RaisePropertyChanged(); }
        }

        private async void Delete(ToDoDto dto)
        {
           var deleteResult=await toDoService.DeleteAsync(dto.Id);
            if (deleteResult.Status)
            {
                //在当前数据集合中,找到当前已经删除掉的数据,并移除掉
               var model= ToDoDtos.FirstOrDefault(t => t.Id.Equals(dto.Id));
                if(model != null) ToDoDtos.Remove(model);
            }
        }
    }
}

7.当查找不到数据时,希望显示一个默认的图片。例如暂无数据。。

ToDoView.xaml 前端页面修改,先添加一张图片。然后,如何判断是否有无数据。是通过添加转换器,拿到ToDoDtos 数据集合统计总数是否为0来显示或隐藏当前图片。随便找一张图片放在Image文件夹中

 <!--当查不到数据时,要显示的图片。添加转换器来控制,要不要显示这个图片-->
<StackPanel Grid.Row="1" VerticalAlignment="Center" Visibility="{Binding ToDoDtos.Count,Converter={StaticResource IntToVisibility}}">
    <Image Source="/Images/NoData.png" Width="620" Height="220"/>
    <TextBlock Margin="0,10" FontSize="18" HorizontalAlignment="Center" Text="哇哦,暂无数据"/>
</StackPanel>

添加转换器 IntToVisibilityConveter,需继承自 IValueConverter。

namespace MyToDo.Common.Converters
{
    /// <summary>
    /// 转换器
    /// </summary>
    public class IntToVisibilityConveter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if(value!=null && int.TryParse(value.ToString(), out int result))
            {
                if(result ==0) return Visibility.Visible; //如果等于0,则让图片显示
            }
            //否则,隐藏图片
            return Visibility.Hidden;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

转换器编写完成后,然后添加到 ToDoView.xaml 前端当中进行使用。

首先引入转换器命名空间

 xmlns:cv="clr-namespace:MyToDo.Common.Converters"

然后,还需要在当前ToDoView.xaml 用户控件的资源文件中,进行声明。

   <UserControl.Resources>
       <cv:IntToVisibilityConveter x:Key="IntToVisibility"/>
   </UserControl.Resources>

8.根据下拉列表选择的状态值查询 

同搜索框查询处理方式一样,然后下拉列表 ComboBox 也需要绑定一个属性。那么首先要定义这个属性,然后在ToDoView.xaml 中的选择框中,绑定这个 SelectIndex 属性

<ComboBox SelectedIndex="{Binding SelectIndex}">
    <ComboBoxItem>全部</ComboBoxItem>
    <ComboBoxItem>待办</ComboBoxItem>
    <ComboBoxItem>已完成</ComboBoxItem>
</ComboBox>

后台 ToDoViewModel 逻辑中,需要去定义这个 SelectIndex 属性。由于基类没有根据该条件的查询,所以我们可以自定义自己的查询接口和参数,从而避免修改到基类共用类的方法函数。

namespace MyToDo.ViewModels
{
   public class ToDoViewModel: NavigationViewModel
    {
        public ToDoViewModel(IToDoService toDoService, IContainerProvider provider):base(provider)
        {
            ToDoDtos = new ObservableCollection<ToDoDto>();
            this.toDoService = toDoService;
        }

        private ObservableCollection<ToDoDto> toDoDtos;
        private readonly IToDoService toDoService;

        /// <summary>
        /// 创建数据的动态集合
        /// </summary>
        public ObservableCollection<ToDoDto> ToDoDtos
        {
            get { return toDoDtos; }
            set { toDoDtos = value;RaisePropertyChanged(); }
        }
      
        private string search;
        /// <summary>
        /// 用户输入的搜索条件
        /// </summary>
        public string Search
        {
            get { return search; }
            set { search = value; RaisePropertyChanged(); }
        }
        private int selectIndex=0;
        /// <summary>
        /// 下拉列表状态值
        /// </summary>
        public int SelectIndex
        {
            get { return selectIndex; }
            set { selectIndex = value; RaisePropertyChanged(); }
        }



        /// <summary>
        /// 获取数据
        /// </summary>
        async void GetDataAsync()
        {
            UpdateLoading(true); //发布消息,设置加载中的窗口
            //前端界面 0全部,1 待办,2 已完成;数据库实际值,0待办,1已完成
            int? stastus=  SelectIndex == 0 ? null : SelectIndex == 2 ? 1 : 0;
            //添加查询条件
            var todoResult=await toDoService.GetAllFilterAsync(new Shared.Parameters.ToDoParameter()
            {
                PageIndex = 0,
                PageSize = 100,
                Search = Search, //传入搜索框查询条件
                Status=selectIndex //下拉框值
            });
            if (todoResult.Status)
            {
                toDoDtos.Clear();
                foreach (var item in todoResult.Result.Items)
                {
                    toDoDtos.Add(item);
                }
            }
            UpdateLoading(false); //发布消息,关闭加载中的窗口
        }

        //重写导航加载数据的方法
        public override void OnNavigatedTo(NavigationContext navigationContext)
        {
            base.OnNavigatedTo(navigationContext);
            GetDataAsync();
        }
    }
}

自定义查询接口和参数实现,例如:当前根据下拉列表状态查询,由于基类方法不适用。需要新增一个自定义的查询接口和参数

 步骤1.MyToDo.Shared 项目中,定义一个 ToDoParameter 类,继承自QueryParameter 类
  public class ToDoParameter: QueryParameter
  {
      public int? Status { get; set; }
  }

步骤2.在MyToDo 项目Service 文件中,增加自定义的查询接口,并传入定义的参数类
namespace MyToDo.Service
{
    public interface IToDoService:IBaseService<ToDoDto>
    {
        Task<ApiResponse<PagedList<ToDoDto>>> GetAllFilterAsync(ToDoParameter parameter);
    }
}

步骤3.ToDoService 中去实现自定义的接口,查询条件就加上自定义根据状态来查
namespace MyToDo.Service
{
    public class ToDoService : BaseService<ToDoDto>, IToDoService
    {
        private readonly HttpRestClient client;

        /// <summary>
        /// 构造中,直接传控制器名称进去。因为在Web Api项目中,待办事项控制器的名称,就是叫ToDo
        /// </summary>
        /// <param name="client"></param>
        /// <param name="serverName"></param>
        public ToDoService(HttpRestClient client, string serverName= "ToDo") : base(client, serverName)
        {
            this.client = client;
        }

        public async Task<ApiResponse<PagedList<ToDoDto>>> GetAllFilterAsync(ToDoParameter parameter)
        {
            var request = new BaseRequest()
            {
                Method = Method.Get,
                Route = $"api/ToDo/GetAllFilter?pageIndex={parameter.PageIndex}" +
                $"&pageSize={parameter.PageSize}&Search={parameter.Search}&Status={parameter.Status}"
            };
            return await client.ExecuteAsync<PagedList<ToDoDto>>(request);
        }
    }
}

步骤4.接着,修改MyToDo.Api 项目,首先IToDoService 新增一个 GetAllFilter 接口
namespace MyToDo.Api.Service
{
    public interface IToDoService: IBaseService<ToDoDto>
    {
        Task<ApiResponse> GetAllFilterAsync(ToDoParameter query);
    }
}

步骤5.接着,在MyToDo.Api 项目 去ToDoService层实现 GetAllFilterAsync 接口
 public async Task<ApiResponse> GetAllFilterAsync(ToDoParameter query)
 {
     try
     {
         var todos = await work.GetRepository<ToDo>()
              //根据标题查,如果传过来的Search 为空,直接过。否则就匹配标题。
              .GetPagedListAsync(predicate: x => (string.IsNullOrWhiteSpace(query.Search) ? true : x.Title.Contains(query.Search))
                && (query.Status==null ? true : x.Status.Equals(query.Status)),
               pageIndex: query.PageIndex,
               pageSize: query.PageSize,
               orderBy: source => source.OrderByDescending(t => t.CreateDate) //根据创建时间进行排序
               );
         return new ApiResponse(true, todos); //返回true,并返回所有数据
     }
     catch (Exception ex)
     {
         return new ApiResponse(ex.Message);
     }
 }

步骤6.然后还要在MyToDo.Api 项目中,修改ToDoController 控制器,添加GetAllFilter 接口方法,并传入自定义的参数。
[HttpGet]
public async Task<ApiResponse> GetAllFilter([FromQuery] ToDoParameter query) => await service.GetAllFilterAsync(query);

步骤7.最后,在 MyToDo 项目中的 ToDoViewModel 逻辑处理层去使用它
  /// <summary>
  /// 获取数据
  /// </summary>
  async void GetDataAsync()
  {
      UpdateLoading(true); //发布消息,设置加载中的窗口
  //前端界面 0全部,1 待办,2 已完成;数据库实际值,0待办,1已完成
   int? stastus=  SelectIndex == 0 ? null : SelectIndex == 2 ? 1 : 0;
      //添加查询条件
      var todoResult=await toDoService.GetAllFilterAsync(new Shared.Parameters.ToDoParameter()
      {
          PageIndex = 0,
          PageSize = 100,
          Search = Search, //传入搜索框查询条件
          Status=selectIndex //下拉框值
      });
      if (todoResult.Status)
      {
          toDoDtos.Clear();
          foreach (var item in todoResult.Result.Items)
          {
              toDoDtos.Add(item);
          }
      }
      UpdateLoading(false); //发布消息,关闭加载中的窗口
  }


二.完整源码

ToDoView.xaml 

<UserControl x:Class="MyToDo.Views.ToDoView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
             xmlns:local="clr-namespace:MyToDo.Views"
             xmlns:cv="clr-namespace:MyToDo.Common.Converters"
             mc:Ignorable="d" 
             xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <cv:IntToVisibilityConveter x:Key="IntToVisibility"/>
    </UserControl.Resources>
    <md:DialogHost>
        <md:DrawerHost IsRightDrawerOpen="{Binding IsRightDrawerOpen}">
            <!--设计右边弹出层-->
            <md:DrawerHost.RightDrawerContent>
                <!--定义弹出层的内容区域-->
                <DockPanel Width="300" LastChildFill="False">
                    <TextBlock Text="添加待办" Padding="20,10" FontSize="20" FontWeight="Bold"  DockPanel.Dock="Top"/>
                    <StackPanel Orientation="Horizontal" Margin="20" DockPanel.Dock="Top">
                        <TextBlock Text="状态:"  Padding="0,0,10,0" VerticalAlignment="Center"/>
                        <ComboBox SelectedIndex="{Binding CurrentDto.Status}"> <!--通过绑定索引来找到对应的状态-->
                            <ComboBoxItem>待办</ComboBoxItem>
                            <ComboBoxItem>已完成</ComboBoxItem>
                        </ComboBox>
                    </StackPanel>
                    <TextBox Text="{Binding CurrentDto.Title}" md:HintAssist.Hint="请输入待办概要" Margin="20,0" DockPanel.Dock="Top"/>
                    <TextBox Text="{Binding CurrentDto.Content}" md:HintAssist.Hint="请输入待办内容" Margin="20" MinHeight="100" DockPanel.Dock="Top"/>
                    <Button Command="{Binding ExecuteCommand}" CommandParameter="保存" Content="添加到待办"  DockPanel.Dock="Top" Margin="20,0" />
                </DockPanel>
            </md:DrawerHost.RightDrawerContent>

            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto"/>
                    <RowDefinition/>
                </Grid.RowDefinitions>

                <StackPanel Margin="15,0,0,0" Orientation="Horizontal">
                    <!--设置绑定模式和更新数据源类型-->
                    <TextBox Text="{Binding Search,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Width="250" VerticalAlignment="Center" md:HintAssist.Hint="查找待办事项..." md:TextFieldAssist.HasClearButton="True">
                        <!--搜索框绑定回车事件-->
                        <TextBox.InputBindings>
                            <!--通过Key 绑定-->
                            <KeyBinding Key="Enter"  Command="{Binding ExecuteCommand}" CommandParameter="查询"/>
                        </TextBox.InputBindings>
                    </TextBox>
                    <TextBlock Text="筛选:" Margin="10.0" VerticalAlignment="Center"/>
                    <ComboBox SelectedIndex="{Binding SelectIndex}">
                        <ComboBoxItem>全部</ComboBoxItem>
                        <ComboBoxItem>待办</ComboBoxItem>
                        <ComboBoxItem>已完成</ComboBoxItem>
                    </ComboBox>
                </StackPanel>
                <Button HorizontalAlignment="Right" Content="+ 添加待办" Margin="10,5" Command="{Binding ExecuteCommand}" CommandParameter="新增" />
                 <!--当查不到数据时,要显示的图片。添加转换器来控制,要不要显示这个图片-->
                <StackPanel Grid.Row="1" VerticalAlignment="Center" Visibility="{Binding ToDoDtos.Count,Converter={StaticResource IntToVisibility}}">
                    <Image Source="/Images/NoData.png" Width="620" Height="220"/>
                    <TextBlock Margin="0,10" FontSize="18" HorizontalAlignment="Center" Text="哇哦,暂无数据"/>
                </StackPanel>
                <ScrollViewer Grid.Row="1">
                    <ItemsControl  HorizontalAlignment="Center" ItemsSource="{Binding ToDoDtos}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <WrapPanel />
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <!--自定义内容模板-->
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <md:TransitioningContent OpeningEffect="{md:TransitionEffect Kind=ExpandIn}">
                                <!--自定义内容区域-->
                                <Grid Width="220" MinHeight="180" MaxHeight="250" Margin="8" >
                                    <!--行为触发器-->
                                    <i:Interaction.Triggers>
                                             <!--鼠标左击事件-->
                                            <i:EventTrigger EventName="MouseLeftButtonUp">
                                                <!--设置命令-->
                                                <i:InvokeCommandAction 
                                                    CommandParameter="{Binding}"
                                                    Command="{Binding DataContext.SelectedCommand ,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}}"/>
                                            </i:EventTrigger>
                                    </i:Interaction.Triggers>
                                    <!--定义2行-->
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="auto"/>
                                        <RowDefinition />
                                    </Grid.RowDefinitions>
                                    <!--右上角按钮-->
                                    <md:PopupBox HorizontalAlignment="Right" Panel.ZIndex="1">
                                            <Button Content="删除"  CommandParameter="{Binding}"
                                             Command="{Binding DataContext.DeleteCommand ,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}}"/>
                                    </md:PopupBox>

                                    <!--整个框圆角-->
                                        <Border CornerRadius="3" Grid.RowSpan="2" >
                                            <!--增加触发器,根据绑定的状态不同,显示不同的颜色-->
                                            <Border.Style>
                                                <Style TargetType="Border">
                                                    <Style.Triggers>
                                                        <DataTrigger Binding="{Binding Status}" Value="0">
                                                            <Setter Property="Background" Value="#3CB371"/>
                                                        </DataTrigger>
                                                        <DataTrigger Binding="{Binding Status}" Value="1">
                                                            <Setter Property="Background" Value="#1E90EF"/>
                                                        </DataTrigger>
                                                    </Style.Triggers>
                                                </Style>
                                            </Border.Style>
                                        </Border>

                                        <TextBlock  Text="{Binding Title}" Padding="10,5" FontWeight="Bold"/>
                                    <TextBlock Text="{Binding Content}" Padding="10,5" Grid.Row="1"/>
                                    <!--白色背景底色控件-->
                                    <Canvas Grid.RowSpan="2" ClipToBounds="True">
                                        <Border Canvas.Top="10" CornerRadius="100" Canvas.Right="-50" Width="120" Height="120" Background="#ffffff" Opacity="0.1"/>
                                        <Border Canvas.Top="80" CornerRadius="100" Canvas.Right="-30" Width="120" Height="120" Background="#ffffff" Opacity="0.1"/>
                                    </Canvas>
                                </Grid>
                                </md:TransitioningContent>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </ScrollViewer>
                
            </Grid>

        </md:DrawerHost>
        
    </md:DialogHost>
   
</UserControl>

ToDoViewModel.cs

namespace MyToDo.ViewModels
{
   public class ToDoViewModel: NavigationViewModel
    {
        //由于NavigationViewModel 类构造中传入了 IOC容器,所以当前类继承的时候,需要把对应的参数传通过Base传过去就不会报错了
        public ToDoViewModel(IToDoService toDoService, IContainerProvider provider):base(provider)
        {
            ToDoDtos = new ObservableCollection<ToDoDto>();
            ExecuteCommand = new DelegateCommand<string>(Execute);
            SelectedCommand = new DelegateCommand<ToDoDto>(Selected);
            DeleteCommand = new DelegateCommand<ToDoDto>(Delete);
            this.toDoService = toDoService;
        }

        private bool isRightDrawerOpen;
        /// <summary>
        /// 右侧编辑窗口是否展开
        /// </summary>
        public bool IsRightDrawerOpen
        {
            get { return isRightDrawerOpen; }
            set { isRightDrawerOpen = value; RaisePropertyChanged(); }
        }


        public DelegateCommand<string> ExecuteCommand{ get; private set; }
        public DelegateCommand<ToDoDto> SelectedCommand { get; private set; }
        public DelegateCommand<ToDoDto> DeleteCommand { get; private set; }
        private ObservableCollection<ToDoDto> toDoDtos;
        private readonly IToDoService toDoService;

        /// <summary>
        /// 创建数据的动态集合
        /// </summary>
        public ObservableCollection<ToDoDto> ToDoDtos
        {
            get { return toDoDtos; }
            set { toDoDtos = value;RaisePropertyChanged(); }
        }
        private ToDoDto currentDto;
        /// <summary>
        /// 编辑选中/新增对象
        /// </summary>
        public ToDoDto CurrentDto
        {
            get { return currentDto; }
            set { currentDto = value; RaisePropertyChanged(); }
        }

        private string search;
        /// <summary>
        /// 用户输入的搜索条件
        /// </summary>
        public string Search
        {
            get { return search; }
            set { search = value; RaisePropertyChanged(); }
        }
        private int? selectIndex = 0;
        /// <summary>
        /// 下拉列表状态值
        /// </summary>
        public int? SelectIndex 
        {
            get { return selectIndex; }
            set { selectIndex = value; RaisePropertyChanged(); }
        }



        /// <summary>
        /// 获取数据
        /// </summary>
        async void GetDataAsync()
        {
            UpdateLoading(true); //发布消息,设置加载中的窗口
            //前端界面 0全部,1 待办,2 已完成;数据库实际值,0待办,1已完成
             int? stastus=  SelectIndex == 0 ? null : SelectIndex == 2 ? 1 : 0;
            //添加查询条件
            var todoResult=await toDoService.GetAllFilterAsync(new Shared.Parameters.ToDoParameter()
            {
                PageIndex = 0,
                PageSize = 100,
                Search = Search, //传入搜索框查询条件
                Status= stastus //下拉框值
            });
            if (todoResult.Status)
            {
                toDoDtos.Clear();
                foreach (var item in todoResult.Result.Items)
                {
                    toDoDtos.Add(item);
                }
            }
            UpdateLoading(false); //发布消息,关闭加载中的窗口
        }
        /// <summary>
        /// 添加待办
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        private void Add()
        {
            CurrentDto = new ToDoDto();//添加时,初始化一个新对象
            IsRightDrawerOpen = true;
        }
        private async void Save()
        {
            //判断数据是否为空
            if (string.IsNullOrWhiteSpace(CurrentDto.Title) || string.IsNullOrWhiteSpace(CurrentDto.Content)) return;
            UpdateLoading(true);
            try
            {
                if (CurrentDto.Id > 0) //Id 大于0,表示编辑。否则新增
                {
                    var updateResult = await toDoService.UpdateAsync(CurrentDto);
                    if (updateResult.Status) //更新成功
                    {
                        //查找到当前界面更新的那个条数据,把显示的内容进行更新
                        var todo = ToDoDtos.FirstOrDefault(t => t.Id == CurrentDto.Id);
                        if (todo != null)
                        {
                            todo.Title = CurrentDto.Title;
                            todo.Content = CurrentDto.Content;
                            todo.Status = CurrentDto.Status;
                        }
                        IsRightDrawerOpen = false; //关闭编辑窗口
                    }
                }
                else
                {
      
                    var addResult = await toDoService.AddAsync(CurrentDto);
                    if (addResult.Status)
                    {
                        if(addResult.Result != null)
                        {
                            ToDoDtos.Add(addResult.Result); //把数据添加到界面的集合中
                            IsRightDrawerOpen = false; //关闭新增窗口
                        } 
                    }
                }
            }
            catch (Exception ex)
            {
                await Console.Out.WriteLineAsync(ex.Message);
            }
            finally
            {
                UpdateLoading(false);
            }
        }

        private async void Delete(ToDoDto dto)
        {
           var deleteResult=await toDoService.DeleteAsync(dto.Id);
            if (deleteResult.Status)
            {
                //在当前数据集合中,找到当前已经删除掉的数据,并移除掉
               var model= ToDoDtos.FirstOrDefault(t => t.Id.Equals(dto.Id));
                if(model != null) ToDoDtos.Remove(model);
            }
        }
        /// <summary>
        /// 根据不同的参数,处理不同的逻辑
        /// </summary>
        /// <param name="obj"></param>
        private void Execute(string obj)
        {
            switch (obj)
            {
                case "新增":
                    Add();
                    break;
                case "查询":
                    GetDataAsync();
                    break;
                case "保存":
                    Save();
                    break;
            }
        }
        private async void Selected(ToDoDto obj)
        {
            try
            {
                UpdateLoading(true);
             
                //进行数据查询
                var todoResult = await toDoService.GetFirstOfDefaultAsync(obj.Id);
                if (todoResult.Status)
                {
                    //把拿到的结果,赋给一个当前选中的ToDoDto
                    CurrentDto = todoResult.Result;
                    IsRightDrawerOpen = true;//打开窗口
                }
            }
            catch (Exception ex)
            {
                await Console.Out.WriteLineAsync(ex.Message);
            }
            finally
            {
                UpdateLoading(false);
            }
          
        }

        //重写导航加载数据的方法
        public override void OnNavigatedTo(NavigationContext navigationContext)
        {
            base.OnNavigatedTo(navigationContext);
            GetDataAsync();
        }
    }
}

整个项目结构 ,其他有小改动的接口都在错误排查里面体现了。贴源码太多了。


三.当前章节所有出现的错误排查

1.查询报错,是Dto 引用不对,应该引用的是MyToDo.Shared

如果运行点击查询报错,一定要检查,所有的接口使用的Dto是不是MyToDo.Shared 里面的Dto,而不是引用MyToDo下面Models 文件夹中的Dto。或者说,直接把 MyToDo 下面的Models 文件夹中的Dto 类型删除掉。重新引用接口报错的Dto 类就可以了。

例如:ToDoService 接口处理,重新引用Dto。同其他的报错的ViewMode类 一样处理,重新引用一下Dto就可以了。

2.待办事项接口查询一直无数据返回

  • 检查待办事项接口,获取仓储时,传的实体是否是ToDo(待办事项实体)

  • 检查待办事项请求的Method 类型是否设置正确

3.搜索框输入关键词查询,一直无数据返回

修改待办事项查询接口,根据标题关键词查询,是使用 Contains(包含)而不是Equals(等于)

4.新增总是返回 status 400报不错,导致没办新增或更新成功

将原来添加请求参数 request.AddParameter 替换成 request.AddJsonBody。主要参考官网例子 。可能大概是请求传参数处理的有问题或其他原因,暂时不管了。

5.实际数据库已更新成功,但代码执行总是报错

在 ToDoController 中,修改更新逻辑,更新成功后把Todo实体返回出去。这样在序列化的时候才能不报错。如果直接返回一个字符串:例如:更新成功。会导致序列化出错,从而影响到前端界面显示。

6.添加成功的待办,无法再次编辑

由于原来新增成功后,返回的实体是传递过来的 model实体,ID属性值为空,导致该问题。只需要把添加成功的新实体返回即可。

7.待办事项删除报错 

把通用删除接口返回 ApiResponse修改成 ApiResponse<TEntity> 泛型类

同时在MyToDo.Api ToDoService 中,修改删除 DeleteAsync接口逻辑,把删除的实体返回出去即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/547004.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

泰迪智能科技携手韩山师范学院“企业微专业合作办学招生宣讲”圆满结束

为进一步深化校企合作&#xff0c;落实高校应用型人才培养。2024年4月11日&#xff0c;泰迪智能科技携手韩山师范学院开展企业微专业合作办学招生宣讲会在韩山师范学院顺利举行&#xff0c;本次宣讲会旨在与韩山师范学院学子深入讲解数字经济时代下的企业用工需求&#xff0c;着…

ins视频批量下载,instagram批量爬取视频信息

简介 Instagram 是目前最热门的社交媒体平台之一,拥有大量优质的视频内容。但是要逐一下载这些视频往往非常耗时。在这篇文章中,我们将介绍如何使用 Python 编写一个脚本,来实现 Instagram 视频的批量下载和信息爬取。 我们使用selenium获取目标用户的 HTML 源代码,并将其保存…

数据结构 -- 二分查找

本文主要梳理了二分查找算法的几种实现思路&#xff0c;基本概念参考 顺序、二分、哈希查找的区别及联系_生成一个大小为10万的有序数组,随机查找一个元素,分别采用顺序查找和二分查找方式-CSDN博客 1、基本概念 &#xff08;1&#xff09;前提条件&#xff1a;待查找数据必须…

解决调用相同url数据不刷新问题

原代码 原因 谷歌浏览访问相同接口默认调用缓存数据 解决方案 添加时间戳

WebKit简介及工作流程

文章目录 一、WebKit简介二、WebKit结构三、Webkit工作流程四、WebKit常见问题五、WebKit优点六、相关链接 一、WebKit简介 WebKit是一个开源的浏览器引擎&#xff0c;它的起源可以追溯到2001年&#xff0c;当时苹果公司推出了其首款基于Unix的操作系统Mac OS X。在2002年&…

科大讯飞星火开源大模型iFlytekSpark-13B GPU版部署方法

星火大模型的主页&#xff1a;iFlytekSpark-13B: 讯飞星火开源-13B&#xff08;iFlytekSpark-13B&#xff09;拥有130亿参数&#xff0c;新一代认知大模型&#xff0c;一经发布&#xff0c;众多科研院所和高校便期待科大讯飞能够开源。 为了让大家使用的更加方便&#xff0c;科…

Golang | Leetcode Golang题解之第30题串联所有单词的子串

题目&#xff1a; 题解&#xff1a; func findSubstring(s string, words []string) (ans []int) {ls, m, n : len(s), len(words), len(words[0])for i : 0; i < n && im*n < ls; i {differ : map[string]int{}for j : 0; j < m; j {differ[s[ij*n:i(j1)*n]…

分布式的计算框架之Spark(python第三方库视角学习PySpark)

基本介绍 Apache Spark是专为大规模数据处理而设计的快速通用的计算引擎 。现在形成一个高速发展应用广泛的生态系统。 特点介绍 Spark 主要有三个特点&#xff1a; 首先&#xff0c;高级 API 剥离了对集群本身的关注&#xff0c;Spark 应用开发者可以专注于应用所要做的计…

牛客网刷题:BC48 牛牛的线段

输入描述&#xff1a; 第一行输入 x1 和 y1&#xff0c;用空格隔开。 第二行输入 x2 和 y2&#xff0c;用空格隔开。 其中 x1 &#xff0c; y1 &#xff0c;x2 &#xff0c;y2 都是整数 输出描述&#xff1a; 输出线段的长度的平方 解题思路&#xff1a; 定义四个变量 用…

【黑马头条】-day06自媒体文章上下架-Kafka

文章目录 今日内容1 Kafka1.1 消息中间件对比1.2 kafka介绍1.3 kafka安装及配置1.4 kafka案例1.4.1 导入kafka客户端1.4.2 编写生产者消费者1.4.3 启动测试1.4.4 多消费者启动 1.5 kafka分区机制1.5.1 topic剖析 1.6 kafka高可用设计1.7 kafka生产者详解1.7.1 同步发送1.7.2 异…

【C 数据结构】栈

文章目录 【 1. 基本原理 】栈的分类 【 2. 动态链表栈 】2.1 双结构体实现2.1.0 栈的节点设计2.1.1 入栈2.1.2 出栈2.1.3 遍历2.1.4 实例 2.2 单结构体实现2.2.0 栈的节点设计2.2.1 入栈2.2.2 出栈2.2.3 实例 【 3. 顺序栈 】3.1 入栈3.2 出栈3.3 实例 【 1. 基本原理 】 栈&…

操作系统:进程(二)

进程的状态 进程状态反映进程执行过程的变化。这些状态随着进程的执行和外界条件的变化而转换。在三态模型中&#xff0c;进程状态分为三个基本状态&#xff0c;即运行态&#xff0c;就绪态&#xff0c;阻塞态。 一个进程从创建而产生至撤销而消亡的整个生命期间&#xff0c;…

【图像分类】基于深度学习的轴承和齿轮识别(ResNet网络)

写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。(专栏订阅用户订阅专栏后免费提供数据集和源码一份,超级VIP用户不在服务范围之内,不想订阅专栏的兄弟们可以私信…

java的深入探究JVM之类加载与双亲委派机制

前言 前面学习了虚拟机的内存结构、对象的分配和创建&#xff0c;但对象所对应的类是怎么加载到虚拟机中来的呢&#xff1f;加载过程中需要做些什么&#xff1f;什么是双亲委派机制以及为什么要打破双亲委派机制&#xff1f; 类的生命周期 类的生命周期包含了如上的7个阶段&a…

A complete evaluation of the Chinese IP geolocation databases(2015年)

下载地址:A Complete Evaluation of the Chinese IP Geolocation Databases | IEEE Conference Publication | IEEE Xplore 被引用次数:12 Li H, He Y, ** R, et al. A complete evaluation of the Chinese IP geolocation databases[C]//2015 8th International Conference…

MyBatis 源码分析系列文章导读

1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章。本篇文章从 MyBatis 是什么&#xff08;what&#xff09;&#xff0c;为什么要使用&#xff08;why&#xff09;&#xff0c;以及如何使用&#xff08;how&#xff09;等三个角度进行了说明和演…

异地组网如何安装?

【天联】是一款强大的异地组网安装工具&#xff0c;可以帮助企业实现远程设备的统一管理和协同办公。以下是【天联】可以应用的一些场景&#xff1a; 零售、收银软件应用统一管理&#xff1a;【天联】可以结合医药、餐饮、商超等零售业的收银软件&#xff0c;实现异地统一管理。…

TongRds docker 镜像做成与迁移(by liuhui)

TongRds docker 镜像做成与迁移 一&#xff0c;使用 docker commit 命令制作 TongRds docker 镜 像 1.1 拉取基础镜像 centos 并运行该镜像 拉取镜像&#xff1a;docker pull ubuntu 镜像列表&#xff1a;docker images 运行镜像&#xff1a;docker run -itd --name myubuntu…

吴恩达2022机器学习专项课程(一) 第二周课程实验:使用 scikit-learn 进行线性回归(Lab_05 Lab_06)

目标 使用scikit-learn实现线性回归(SGDRegressor和LinearRegression)。 1.什么是scikit-learn? 一个用于 Python 编程语言的开源机器学习库,用于实现各种机器学习算法。 2.特征缩放&#xff08;Z标准化&#xff09; 第一步先使用Z标准化处理训练样本&#xff0c;减少训练…

C#创建随机更换背景图片的窗体的方法:创建特殊窗体

目录 一、涉及到的知识点 1.图片资源管理器设计Resources.Designer.cs 2.把图片集按Random.Next方法随机化 3.BackgroundImage属性 二、实例设计 1. Resources.Designer.cs 2.Form1.Designer.cs 3.Form1.cs 4.生成效果 很多时候&#xff0c;我们需要每次打开窗体时能够…