向另一个窗口显示所选对象的对象详细信息用户控件使用 mwwm

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

我有一个从数据库填充的人员列表,人员对象中的两个字段(id,名称)通过单选按钮进行数据绑定,现在当我单击单选按钮时,我将打开一个新的 UserControl 并尝试显示UserControl 中所选对象的详细信息。 我如何在 mvvm 中实现这个?

人物模型:

    namespace Persons.Models
    {
    class PersonModel
    {
    public int PersonID { get; set; }
    public string Name { get; set; }
    public string Surname {get; set; }
    public string PersonAge { get; set; }
    public string PersonAddress { get; set; }
    public string PersonDepartmentID{ get; set; }
    public string PersonGroupID{ get; set; }
    public string PersonContact{ get; set; }
    public string PersonLevelEductationID{ get; set; }
    public string PersonDivisionID{get; set; }

    public ObservableCollection<PersonModel> Get()
    {
        ObservableCollection<PersonModel> Person_list = new ObservableCollection<PersonModel>();
        var Connect = ConfigurationManager.ConnectionStrings["MysqlConnection"].ConnectionString;
        using (var conn = new MySqlConnection(Connect))
        using (var command = new MySqlCommand())
        {
            conn.Open();
            command.Connection = conn;
            command.CommandText = @"SELECT person.id, person.name, person.surname, person.address, person.age, person.contact department.name AS 'department', group.name AS 'group', eductation.lavel division.name AS 'division' FROM person
            LEFT JOIN department ON department.id = person.departmentID LEFT JOIN group ON group.id = person.groupID LEFT JOIN eductation ON eductation.id = person.eductationLavelID LEFT JOIN division ON division.id = person.divisionID";
            MySqlDataReader reader = command.ExecuteReader();
            while (reader.Read())
            {
                Person_list.Add(new PersonModel()
                {
                    PersonID = (int)reader["id"],
                    Name = (string)reader["name"],
                    Surname = (string)reader["surname"],
                    PersonAge = (int)reader["age"],
                    PersonAddress = (string)reader["address"],
                    PersonDepartmentID = (string)reader["department"],
                    PersonGroupID= (string)reader["group"],
                    PersonContact = (string)reader["contact"],
                    PersonLevelEductationID = (string)reader["lavel"],
                    PersonDivisionID = (string)reader["division"]
                }
                    ) ; 
            }
            reader.Close();
            conn.Close();
        }
        return Person_list;
    }
  }
}

查看模型

namespace Persons.ViewModels
{
 class PersonViewModel : ViewModelBase
{
private ObservableCollection<PersonModel> _personList;
private readonly PersonModel _person_Model;

public ObservableCollection<PersonModel> PersonItems
    {
        get => _personList;
        set
        {
            if (_personList!= value)
            {
                _personList = value;
                OnpropertyChanged(nameof(PersonItems));
            }
        }
    }

public PersonViewModel()
    {
        _person_Model= new PersonModel();
        _personList= _person_Model.Get();
    }
 }

}

主要用户控制:

 <ScrollViewer 
            VerticalScrollBarVisibility="Auto">
            <ItemsControl ItemsSource="{Binding PersonItems}">
                <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border 
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="auto"/>
                                    <RowDefinition Height="auto"/>
                                </Grid.RowDefinitions>

                             <RadioButton
                             Content="{Binding Name}"     
                             CommandParameter="{Binding Name}"
                             Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ShowPageCommand}"
                             Style="{StaticResource ItemsButtonStyle}"/>
                            </Grid>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>

和新的用户控件

                   <ScrollViewer 
                    VerticalScrollBarVisibility="Auto">
                    <ItemsControl ItemsSource="{Binding PersonItems}" BorderBrush="White">
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <Border 
                                    Width="600"
                                    MaxHeight="800">
                                <Grid>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="auto"/>
                                        </Grid.RowDefinitions>

                                    <TextBlock 
                                    HorizontalAlignment="Left"
                                    Grid.Row="1"
                                    Grid.Column="0"
                                    Text="Name:" />

                                    <TextBlock
                                    HorizontalAlignment="Right"
                                    Grid.Row="1"
                                    Grid.Column="0"
                                    Text="{Binding Name}" />

                                    <TextBlock 
                                    HorizontalAlignment="Left"
                                    Grid.Row="2"
                                    Text="Surname :" />

                                    <TextBlock
                                    HorizontalAlignment="Right"
                                    Grid.Row="2"
                                    Text="{Binding Surname}"/>
                                    
                                    <TextBlock 
                                    HorizontalAlignment="Left"
                                    Grid.Row="3"
                                    Text="PersonAge :" />

                                    <TextBlock
                                    HorizontalAlignment="Right"
                                    Grid.Row="3"
                                    Text="{Binding PersonAge }"/>

                                    <TextBlock 
                                    HorizontalAlignment="Left"
                                    Grid.Row="4"
                                    Text="Person Address:" />

                                        <TextBlock
                                    HorizontalAlignment="Right"
                                    Grid.Row="4"
                                    Text="{Binding PersonAddress }"/>

                                    <TextBlock 
                                    HorizontalAlignment="Left"
                                    Grid.Row="6"
                                    Text="Department:" />

                                    <TextBlock
                                    HorizontalAlignment="Right"
                                    Grid.Row="6"
                                    Text="{Binding PersonDepartmentID}"/>
                                    
                                      ........
                                    <TextBlock 
                                    HorizontalAlignment="Left"
                                    Grid.Row="9"
                                    Text="Eductation :" />

                                    <TextBlock
                                    HorizontalAlignment="Right"
                                    Grid.Row="9"
                                    Text="{Binding PersonLevelEductationID}"/>

                                    </Grid>
                                </Border>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>

和命令

  namespace Persons.ViewModels
   {
  class NavigationViewModel : INotifyPropertyChanged
    {
    
        public NavigationViewModel()
        {
            // Set Startup Page
            SelectedViewModel = new StartupViewModel();
        
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
        }

        // Select ViewModel
        private object _selectedViewModel;
        public object SelectedViewModel
        {
            get => _selectedViewModel;
            set { _selectedViewModel = value; OnPropertyChanged("SelectedViewModel"); }
        }
        public void CrudView()
        {
            SelectedViewModel = new CrudPage();    // new usercontrol
        }
        private ICommand _CrudCommand;
        public ICommand ShowPageCommand
        {
            get
            {
                if (_CrudCommand == null)
                {
                    _CrudCommand = new RelayCommand(param => CrudView());
                }
                return _CrudCommand;
            }
        }
      
        // Close App
        public void CloseApp(object obj)
        {
            MainWindow win = obj as MainWindow;
            win.Close();
        }

        // Close App Command
        private ICommand _closeCommand;
        public ICommand CloseAppCommand
        {
            get
            {
                if (_closeCommand == null)
                {
                    _closeCommand = new RelayCommand(p => CloseApp(p));
                }
                return _closeCommand;
            }
        }
       }

RelayCommand类

 namespace Persons.Utilities
 {
 public class RelayCommand : ICommand
{
    private Action<object> execute;
    private Func<object, bool> canExecute;

    public RelayCommand(Action<object> execute)
    {
        this.execute = execute;
        canExecute = null;
    }

    public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }


    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return canExecute == null || CanExecute(parameter);
    }

    public void Execute(object parameter)
    {
        execute(parameter);
    }
}

对于长代码,我深表歉意!

c# wpf mvvm
1个回答
0
投票

您所追求的称为主从视图。您可以通过使用

ContentControl
显示详细信息视图来实现此目的。
您可以将详细信息视图的数据模型分配给
ContentControl.Content
属性,并定义
DatatTemplate
来实际渲染视图。您可以将此模板分配给
ContentControl.Contenttemplate
属性或将
DataTemplate
定义为隐式,以便自动加载。

由于加载是由

Button
触发的,因此您可以将详细信息模型作为命令参数传递,或者将
ListBox.SelectedItem
属性绑定到也定义
ICommand
的视图模型。

一些注意事项:

  • 您的
    PersonModel
    不应访问数据库来生成自己的列表。相反,拥有
    PersonViewModel
    的视图模型必须调用应用程序模型以返回
    PersonModel
    集合。该模型在内部使用数据库或从文件或服务读取/写入文件或服务。
  • 控件和视图模型类之间不存在 1:1 的关系。换句话说,并不是每个控件都有自己的视图模型。视图模型类代表特定的数据上下文。多个控件使用相同的数据进行操作,因此使用相同的数据上下文。从您的数据绑定中,我可以看出
    UserControl
    设置了错误的
    DataContext
    。此外,您可以避免显式设置 DataContext,而是让它被继承。
  • UserControl
    不应将其内部结构直接绑定到
    DataContext
    。相反,引入内部可以绑定的依赖属性。然后将这些依赖属性绑定到实际的
    DataContext
    。如果您想在不同的地方或应用程序中重用该控件,这一点尤其重要。如果您不需要这种灵活性,您当然可以继续将依赖项硬编码到自定义控件中。我只是指出这不是最佳实践。
  • 尝试对视图模型类使用组合。例如,您可以定义一个
    MainViewModel
    来公开其他视图模型类,例如
    PersonViewModel
    NavigationViewModel
    。然后将
    MainViewModel
    分配给
    DataContext
    根(例如
    MainWindow
    )。这更容易维护并增加灵活性。例如,这样的结构允许您使用依赖项注入,因为唯一要注入的视图模型是将
    MainViewModel
    注入
    MainWindow
  • 在视图模型中创建控件或处理控件违反了 MVVM 设计模式规则。
  • 在视图模型中执行打开或关闭视图等 UI 逻辑违反了 MVVM 设计模式。此类代码属于视图(例如代码隐藏)。例如,要关闭窗口或应用程序,您可以使用通用事件处理程序和
    Button.Click
    事件或使用路由命令和
    Button.Command
    属性。
  • 您不必显式关闭实现
    IDisposable
    的资源。
    using
    语句将为您做到这一点。处理对象将自动关闭它们并刷新缓冲区,例如如果有
    Stream
    物体。

MainUserControl.xaml

<UserControl>

  <!-- Bind to the nested PersonViewModel -->
  <ListBox ItemsSource="{Binding PersonViewModel.PersonItems}">
    <ListBox.ItemTemplate>
      <DataTemplate DataType="{x:Type PersonModel}">
        <Border>
          <Grid>
            <Grid.RowDefinitions>
              <RowDefinition Height="auto" />
              <RowDefinition Height="auto" />
            </Grid.RowDefinitions>

            <!-- Bind the Command to the nested NavigationViewModel -->
            <RadioButton Content="{Binding Name}"
                         CommandParameter="{Binding}"
                         Command="{Binding NavigationViewModel.ShowPageCommand}"
                         Style="{StaticResource ItemsButtonStyle}" />
          </Grid>
        </Border>
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
</UserControl>

PersonModelDetailsView.xaml

<UserControl x:Name="Root">
  <ContentControl Content="{Binding ElementName=Root, Path=PersonModel}"
                  ContentTemplate="{Binding ElementName=Root, Path=PersonModelTemplate}" />
</UserControl>

PersonModelDetailsView.xaml.cs

partial class PersonModelDetailsView : UserControl
{
  public PersonModel PersonModel
  {
    get => (PersonModel)GetValue(PersonModelProperty);
    set => SetValue(PersonModelProperty, value);
  }

  public static readonly DependencyProperty PersonModelProperty = DependencyProperty.Register(
    nameof(PersonModel),
    typeof(PersonModel),
    typeof(PersonModelDetailsView),
    new FrameworkPropertyMetadata(default));

  public DataTemplate PersonModelTemplate
  {
    get => (DataTemplate)GetValue(PersonModelTemplateProperty);
    set => SetValue(PersonModelTemplateProperty, value);
  }

  public static readonly DependencyProperty PersonModelTemplateProperty = DependencyProperty.Register(
    nameof(PersonModelTemplate),
    typeof(DataTemplate),
    typeof(PersonModelDetailsView),
    new FrameworkPropertyMetadata(default));
}

MainWindow.xaml

<Window>
  <Window.DataContext>
    <MainViewModel />
  </Window.DataContext>

  <Window.Resources>
    <DataTemplate x:Key="PersonModelDetailsViewTemplate" 
                  DataType="{x:Type PersonModel}">
      <Border Width="600"
              MaxHeight="800">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
          </Grid.RowDefinitions>

          <TextBlock HorizontalAlignment="Left"
                     Grid.Row="1"
                     Grid.Column="0"
                     Text="Name:" />

          <TextBlock HorizontalAlignment="Right"
                     Grid.Row="1"
                     Grid.Column="0"
                     Text="{Binding Name}" />

          <TextBlock HorizontalAlignment="Left"
                     Grid.Row="2"
                     Text="Surname :" />

          <TextBlock HorizontalAlignment="Right"
                     Grid.Row="2"
                     Text="{Binding Surname}" />
        </Grid>
      </Border>
    </DataTemplate>
  </Window.Resources>

  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" /> <!-- Master view -->
      <ColumnDefinition /> <!-- Details view -->
    <Grid.ColumnDefinitions>
  
    <MainUserControl Grid.Column="0" />
    <PersonModelDetailsView Grid.Column="1"
                            PersonModel="{Binding NavigationViewModel.SelectedPersonModel}"
                            PersonModelTemplate="{StaticResource PersonModelDetailsViewTemplate}" />
  </Grid>
</Window>

MainWindow.xaml.cs

partial class MainWindow : Window
{
  private readonly MainViewModel MainViewModel;

  public MainWindow()
  {
    this.MainViewModel = new MainViewModel();
    this.DataContext = this.MainViewModel;

    InitializeComponent();

    // Initialize the view model classes
    this.Loaded += OnLoaded;
  }

  private void OnLoaded(object sender, RoutedEventArgs e)
  {
    // Initializing all view models from the loaded event is a significant performnace improvement.
    // Just think about doing all of this work from the constructor 
    // where each view model class instantiates its own classes that 
    // themself create instances from their constructor and initialize 
    // them. It would take "ages" for the construction of the top-level 
    // instance to complete, while the UI remains unresponsive. 
    // You can even call async methods which isn't possible in the 
    // constructor. 
    // Every class must perform expensive/long-running initialization 
    // outside the constructor.
    this.MainViewModel.Initialize();
  }

  // TODO::Implement window close and application close logic.
  // The logic that was originally implemented in the view model class.
  // For example use event handlers or routed events or routed commands.
}

MainViewModel.cs

class MainViewModel : INotifyPropertyChanged
{
  public PersonViewModel PersonViewModel { get; } = new PersonViewModel();
  public NavigationViewModel NavigationViewModel { get; } = new NavigationViewModel();

  // Can be async if required
  public void Initialize()
  {
    // Do not make such potentially long-running and blocking calls from the constructor
    this.PersonViewModel.Initialize();
  }
}

PersonViewModel.cs

class PersonViewModel : ViewModelBase
{
  public ObservableCollection<PersonModel> PersonItems { get; }
    
  // The model class to access the database
  private readonly DataRepository dataRepository;

  public PersonViewModel()
  {
    this.PersonItems = new ObservableCollection<PersonModel>();
  }

  // Can be async if required
  public void Initialize()
  {
    // Do not make such potentially long-running and blocking calls from the constructor
    IEnumerable<PersonModel> personModels = this.dataRepository.GetPersonModels();
    foreach (PersonModel personModel in personModels)
    {
      this.PersonItems.Add(personModel);
    }
  }
}

NavigationViewModel.cs

class NavigationViewModel : INotifyPropertyChanged
{
  public NavigationViewModel()
  {
    // Set Startup Page
    SelectedViewModel = new StartupViewModel();
  }

  public event PropertyChangedEventHandler PropertyChanged;
  private void OnPropertyChanged(string propName) 
    => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));

  // Select ViewModel
  private object _selectedViewModel;
  public object SelectedViewModel
  {
    get => _selectedViewModel;
    set { _selectedViewModel = value; OnPropertyChanged(nameof(this.SelectedViewModel)); }
  }

  // Selected PersonModel
  private PersonModel _selectedPersonModel;
  public PersonModel SelectedPersonModel
  {
    get => _selectedPersonModel;
    set { _selectedPersonModel = value; OnPropertyChanged(nameof(this.SelectedPersonModel)); }
  }
  public void ExecuteShowPageCommand(object commandParameter)
  {
    if (commandParameter is not PersonModel detailsPersonModel)
    {
      return;
    }

    // Use data models to navigate.
    // Creating controls in the view model class (like you were doing)
    // is forbidden in MVVM!
    this.SelectedPersonModel = detailsPersonModel;
  }

  private ICommand _CrudCommand;
  public ICommand ShowPageCommand
  {
    get
    {
      if (_CrudCommand == null)
      {
        _CrudCommand = new RelayCommand(ExecuteShowPageCommand);
      }
      return _CrudCommand;
    }
  }

  /* 
    * The following code does not belong into a view model class. 
    * This must be handled by the view! For example in code-behind.
    * You can use event handlers for Button.Click events or routed commands.
    * Then close the Window or the application from there.
    * Handling controls in the view model violates the MVVM design pattern.
    */

  //// Close App
  //public void CloseApp(object obj)
  //{
  //  MainWindow win = obj as MainWindow;
  //  win.Close();
  //}

  //// Close App Command
  //private ICommand _closeCommand;
  //public ICommand CloseAppCommand
  //{
  //  get
  //  {
  //    if (_closeCommand == null)
  //    {
  //      _closeCommand = new RelayCommand(p => CloseApp(p));
  //    }
  //    return _closeCommand;
  //  }
  //}
}

DataRepository.cs
模型类。
数据持久性(数据库和文件 I/O 或服务消耗)始终属于应用程序模型。而不是视图模型或数据模型类。旨在将所有查询放在一个位置(以避免重复代码)。

class DataRepository
{
  public IEnumerable<PersonModel> GetPersonModels()
  {
    var persons = new List<PersonModel>();
    var Connect = ConfigurationManager.ConnectionStrings["MysqlConnection"].ConnectionString;
    using (var conn = new MySqlConnection(Connect))
    using (var command = new MySqlCommand())
    {
      conn.Open();
      command.Connection = conn;
      command.CommandText = @"SELECT person.id, person.name, person.surname, person.address, person.age, person.contact department.name AS 'department', group.name AS 'group', eductation.lavel division.name AS 'division' FROM person
        LEFT JOIN department ON department.id = person.departmentID LEFT JOIN group ON group.id = person.groupID LEFT JOIN eductation ON eductation.id = person.eductationLavelID LEFT JOIN division ON division.id = person.divisionID";
      using MySqlDataReader reader = command.ExecuteReader();
      while (reader.Read())
      {
        Person_list.Add(new PersonModel()
        {
          PersonID = (int)reader["id"],
          Name = (string)reader["name"],
          Surname = (string)reader["surname"],
          PersonAge = (int)reader["age"],
          PersonAddress = (string)reader["address"],
          PersonDepartmentID = (string)reader["department"],
          PersonGroupID = (string)reader["group"],
          PersonContact = (string)reader["contact"],
          PersonLevelEductationID = (string)reader["lavel"],
          PersonDivisionID = (string)reader["division"]
        });
      }
    }
  
    // Don't close these IDisposables. The using-block will handle that.
    //reader. Close();
    //conn.Close();

    return persons;
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.