2016-02-18 2 views
3

Я работаю над проектом WPF и пытаюсь не нарушать концепции MVVM с нулевым кодом в представлениях.Мне нужно перейти к другому представлению, содержащему дочерние объекты родительского объекта в WPF с MVVM

В заключение у меня есть таблица со списком свойств объекта заданий, и я хочу, когда я нажимаю кнопку show logs внутри каждой строки сетки, она показывает мне еще одну сетку, которая содержит журналы для этой работы без нарушения концепции MVVM.

Я хочу показать еще одну сетку, содержащую дочернее свойство, которое представляет собой список объектов, это простая вещь во всех других методах MVC, MVP, но здесь, в MVVM, это что-то странное, я искал это примерно за 20 вопросы и не простое решения

enter image description here

Детали: у меня есть MainView.xaml (Window), JobsView.xaml (UserControl), LogsView.xaml (UserControl) и я имею соответствующие ViewModel для каждого из них.

класс Работа содержит идентификатор, статус, ... и список объектов Log:

public class Job 
{ 
    public Job() 
    { 
     Logs = new List<Log>(); 
    } 
    [Key] 
    public Guid JobID { get; set; } 
    public JobStatus Status { get; set; } 
    public virtual ICollection<Log> Logs { get; set; } 
} 

Я показал JobsView.xaml (UserControl) в MainView.xaml перечислить все задания свойств объектов и я создала пользовательскую кнопку для каждого задания для показа журналов.

<Controls:MetroWindow ...> 
<Grid> 
    <DockPanel> 
      <my:JobView /> 
    </DockPanel> 
</Grid> 

JobView.xaml разметки:

<UserControl x:Class=...> 
<Grid> 
    <DataGrid x:Name="jobsDataGrid" 
       ItemsSource="{Binding Jobs}" 
       SelectedItem="{Binding selectedJob}" 
       AutoGenerateColumns="False" 
       EnableRowVirtualization="True" 
       RowDetailsVisibilityMode="VisibleWhenSelected" 
       IsReadOnly="True"> 
       <DataGrid.Columns> 
      <DataGridTextColumn x:Name="jobIdColumn" 
           Binding="{Binding JobID}" 
           Header="Job Id" 
           Width="SizeToHeader" 
           /> 

      <DataGridTemplateColumn> 
         <DataGridTemplateColumn.CellTemplate> 
          <DataTemplate> 
           <Button Content="Show Logs" 
             Command="{Binding ShowLogsCommand}" 
             /> 
          </DataTemplate> 
         </DataGridTemplateColumn.CellTemplate> 
        </DataGridTemplateColumn> 
       </DataGrid.Columns> 
      </DataGrid> 
</Grid> 

Я хочу, чтобы, когда любое тело нажмите на кнопку Показать Logs это показано пользовательский элемент управления LogsView.xaml внутри MainView.xaml вместо JobsView.

В LogViewModel я есть застройщик, чтобы взять JobId и вернуть журналы:

public class LogViewModel : BindableBase // INotifyPropertyChanged 
{ 
    private Log log = new Log(); 
    private UnitOfWork unitOfWork = new UnitOfWork(); 

    public LogViewModel() 
    { 
     if (DesignerProperties.GetIsInDesignMode(new System.Windows.DependencyObject())) return; 
     Logs = new ObservableCollection<Log>(unitOfWork.Logs.Get(null, ls => ls.OrderBy(l => l.LogID)).ToList()); 
    } 

    public LogViewModel(Guid jobId) 
    { 
     if (DesignerProperties.GetIsInDesignMode(new System.Windows.DependencyObject())) return; 
     Logs = new ObservableCollection<Log>(unitOfWork.Logs.Get(l => l.JobID == jobId, ls => ls.OrderBy(l => l.LogID)).ToList()); 
    } 


    public ObservableCollection<Log> Logs { get; set; } 


    // public event PropertyChangedEventHandler PropertyChanged; 


} 

Но теперь я пытаюсь сделать навигационную службу и пытается несколько методов, но это не работает.

+0

есть ли ответ :( – Marzouk

+0

попытаться спросить здесь https://social.msdn.microsoft.com/Forums/vstudio/en-US/home?forum=wpf – StepUp

+0

Вы ищете 'RowDetailTemplate' HTTP: //www.wpf-tutorial.com/datagrid-control/details-row/ – Eldho

ответ

1

пожалуйста, попробуйте следующее решение:

Xaml (основано на селекторе шаблонов данных)

<Window x:Class="MvvmNavigationIssue.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:mvvmNavigationIssue="clr-namespace:MvvmNavigationIssue" 
    Title="MainWindow" Height="350" Width="525" x:Name="This"> 
<Window.DataContext> 
    <mvvmNavigationIssue:MainNavigationViewModel/> 
</Window.DataContext> 
<Window.Resources> 
    <mvvmNavigationIssue:FreezableProxyClass x:Key="ProxyElement" 
              ProxiedDataContext="{Binding Source={x:Reference This}, Path=DataContext}"/> 
    <DataTemplate x:Key="DefaultDataTemplate"> 
     <Grid> 
      <Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="Tomato" /> 
      <TextBlock Text="Default Template" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock> 
     </Grid> 
    </DataTemplate> 
    <DataTemplate x:Key="JobsDataTemplate"> 
     <ListView ItemsSource="{Binding JobModels, UpdateSourceTrigger=PropertyChanged}"> 
      <ListView.View> 
       <GridView> 
        <GridViewColumn Header="Id" DisplayMemberBinding="{Binding Id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"> 
         <GridViewColumn.CellTemplate> 
          <DataTemplate DataType="mvvmNavigationIssue:JobModel"> 
           <TextBlock Text="{Binding Id}"></TextBlock> 
          </DataTemplate> 
         </GridViewColumn.CellTemplate> 
        </GridViewColumn> 
        <GridViewColumn Header="Title" DisplayMemberBinding="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/> 
        <GridViewColumn Header="Salary" DisplayMemberBinding="{Binding Salary, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/> 
        <GridViewColumn Header="" Width="100"> 
         <GridViewColumn.CellTemplate> 
          <DataTemplate DataType="mvvmNavigationIssue:JobModel"> 
           <Button Command="{Binding Source={StaticResource ProxyElement}, 
            Path=ProxiedDataContext.ShowLogsCommand, Mode=OneWay, 
            UpdateSourceTrigger=PropertyChanged}" CommandParameter="{Binding }">Logs</Button> 
          </DataTemplate> 
         </GridViewColumn.CellTemplate> 
        </GridViewColumn> 
       </GridView> 
      </ListView.View> 
     </ListView> 
    </DataTemplate> 
    <DataTemplate x:Key="LogsDataTemplate"> 
     <ListView ItemsSource="{Binding LogModels, UpdateSourceTrigger=PropertyChanged}"> 
      <ListView.View> 
       <GridView> 
        <GridViewColumn Header="Id" DisplayMemberBinding="{Binding Id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"> 
         <GridViewColumn.CellTemplate> 
          <DataTemplate DataType="mvvmNavigationIssue:JobModel"> 
           <TextBlock Text="{Binding Id}"></TextBlock> 
          </DataTemplate> 
         </GridViewColumn.CellTemplate> 
        </GridViewColumn> 
        <GridViewColumn Header="Title" DisplayMemberBinding="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/> 
        <GridViewColumn Header="Time" DisplayMemberBinding="{Binding LogTime, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/> 
        <GridViewColumn Header="Event" DisplayMemberBinding="{Binding LogEvent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/> 
        <GridViewColumn Header="" Width="100"> 
         <GridViewColumn.CellTemplate> 
          <DataTemplate DataType="mvvmNavigationIssue:JobModel"> 
           <Button Command="{Binding Source={StaticResource ProxyElement}, 
            Path=ProxiedDataContext.ShowAllJobsCommand, Mode=OneWay, 
            UpdateSourceTrigger=PropertyChanged}">All Jobs</Button> 
          </DataTemplate> 
         </GridViewColumn.CellTemplate> 
        </GridViewColumn> 
       </GridView> 
      </ListView.View> 
     </ListView> 
    </DataTemplate> 
    <mvvmNavigationIssue:MainContentTemplateSelector x:Key="MainContentTemplateSelectorKey" 
                DefaultDataTemplate="{StaticResource DefaultDataTemplate}" 
                JobsViewDataTemplate="{StaticResource JobsDataTemplate}" 
                LogsViewDataTemplate="{StaticResource LogsDataTemplate}"/> 
</Window.Resources> 
<Grid> 
    <ContentControl Content="{Binding CurrentViewModel, UpdateSourceTrigger=PropertyChanged}" 
        ContentTemplateSelector="{StaticResource MainContentTemplateSelectorKey}"></ContentControl> 
</Grid> 

MVVM код

public class FreezableProxyClass : Freezable 
{ 
    protected override Freezable CreateInstanceCore() 
    { 
     return new FreezableProxyClass(); 
    } 


    public static readonly DependencyProperty ProxiedDataContextProperty = DependencyProperty.Register(
     "ProxiedDataContext", typeof(object), typeof(FreezableProxyClass), new PropertyMetadata(default(object))); 

    public object ProxiedDataContext 
    { 
     get { return (object)GetValue(ProxiedDataContextProperty); } 
     set { SetValue(ProxiedDataContextProperty, value); } 
    } 
} 

public class MainNavigationViewModel : BaseObservableObject 
{ 
    private object _currentViewModel; 
    private JobsViewModel _jobsViewModel; 
    private List<LogModel> _logModels; 
    private ICommand _showLogs; 
    private ICommand _showJobs; 

    public MainNavigationViewModel() 
    { 
     _jobsViewModel = new JobsViewModel(); 
     Init(); 
    } 

    private void Init() 
    { 
     _jobsViewModel.JobModels = new ObservableCollection<JobModel> 
     { 
      new JobModel{Id = 1, Salary = "12k", Title = "Hw Engineer"}, 
      new JobModel{Id=2, Salary = "18k", Title = "Sw Engineer"}, 
      new JobModel{Id = 3, Salary = "12k", Title = "IT Engineer"}, 
      new JobModel{Id=4, Salary = "18k", Title = "QA Engineer"}, 
     }; 

     _logModels = new List<LogModel> 
     { 
      new LogModel{Id = 1, Salary = "12k", Title = "Hw Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Pending"}, 
      new LogModel{Id = 1, Salary = "12k", Title = "Hw Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Active"}, 
      new LogModel{Id = 1, Salary = "12k", Title = "Hw Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Closed"}, 
      new LogModel{Id=2, Salary = "12k", Title = "Sw Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Pending"}, 
      new LogModel{Id=2, Salary = "12k", Title = "Sw Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Active"}, 
      new LogModel{Id=2, Salary = "12k", Title = "Sw Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Closed"}, 
      new LogModel{Id = 3, Salary = "12k", Title = "IT Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Pending"}, 
      new LogModel{Id = 3, Salary = "12k", Title = "IT Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Active"}, 
      new LogModel{Id = 3, Salary = "12k", Title = "IT Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Closed"}, 
      new LogModel{Id=4, Salary = "12k", Title = "QA Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Pending"}, 
      new LogModel{Id=4, Salary = "12k", Title = "QA Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Active"}, 
      new LogModel{Id=4, Salary = "12k", Title = "QA Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Closed"}, 
     }; 

     CurrentViewModel = _jobsViewModel; 
    } 

    public object CurrentViewModel 
    { 
     get { return _currentViewModel; } 
     set 
     { 
      _currentViewModel = value; 
      OnPropertyChanged(()=>CurrentViewModel); 
     } 
    } 

    public ICommand ShowLogsCommand 
    { 
     get { return _showLogs ?? (_showLogs = new RelayCommand<JobModel>(ShowLogs)); } 
    } 

    private void ShowLogs(JobModel obj) 
    { 
     CurrentViewModel = new LogsViewModel 
     { 
      LogModels = new ObservableCollection<LogModel>(_logModels.Where(model => model.Id == obj.Id)), 
     }; 
    } 

    public ICommand ShowAllJobsCommand 
    { 
     get { return _showJobs ?? (_showJobs = new RelayCommand(ShowAllJobs)); } 
    } 

    private void ShowAllJobs() 
    { 
     CurrentViewModel = _jobsViewModel; 
    } 
} 

public class LogsViewModel:BaseObservableObject 
{ 
    private ObservableCollection<LogModel> _logModels; 

    public ObservableCollection<LogModel> LogModels 
    { 
     get { return _logModels; } 
     set 
     { 
      _logModels = value; 
      OnPropertyChanged(); 
     } 
    } 
} 

public class LogModel : JobModel 
{ 
    private DateTime _logTime; 
    private string _logEvent; 

    public DateTime LogTime 
    { 
     get { return _logTime; } 
     set 
     { 
      _logTime = value; 
      OnPropertyChanged(); 
     } 
    } 

    public string LogEvent 
    { 
     get { return _logEvent; } 
     set 
     { 
      _logEvent = value; 
      OnPropertyChanged(); 
     } 
    } 
} 

public class JobsViewModel:BaseObservableObject 
{ 
    private ObservableCollection<JobModel> _jobModels; 

    public ObservableCollection<JobModel> JobModels 
    { 
     get { return _jobModels; } 
     set 
     { 
      _jobModels = value; 
      OnPropertyChanged(); 
     } 
    } 
} 

public class JobModel:BaseObservableObject 
{ 
    private int _id; 
    private string _title; 
    private string _salary; 

    public int Id 
    { 
     get { return _id; } 
     set 
     { 
      _id = value; 
      OnPropertyChanged(); 
     } 
    } 

    public string Title 
    { 
     get { return _title; } 
     set 
     { 
      _title = value; 
      OnPropertyChanged(); 
     } 
    } 

    public string Salary 
    { 
     get { return _salary; } 
     set 
     { 
      _salary = value; 
      OnPropertyChanged(); 
     } 
    } 
} 

реализация INPC и код команды реле

/// <summary> 
/// implements the INotifyPropertyChanged (.net 4.5) 
/// </summary> 
public class BaseObservableObject : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     var handler = PropertyChanged; 
     if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser) 
    { 
     var propName = ((MemberExpression)raiser.Body).Member.Name; 
     OnPropertyChanged(propName); 
    } 

    protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null) 
    { 
     if (!EqualityComparer<T>.Default.Equals(field, value)) 
     { 
      field = value; 
      OnPropertyChanged(name); 
      return true; 
     } 
     return false; 
    } 
} 

public class RelayCommand : ICommand 
{ 
    private readonly Func<bool> _canExecute; 
    private readonly Action _execute; 

    public RelayCommand(Action execute) 
     : this(() => true, execute) 
    { 
    } 

    public RelayCommand(Func<bool> canExecute, Action execute) 
    { 
     _canExecute = canExecute; 
     _execute = execute; 
    } 

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

    public void Execute(object parameter = null) 
    { 
     _execute(); 
    } 

    public event EventHandler CanExecuteChanged; 
} 

public class RelayCommand<T> : ICommand 
    where T:class 
{ 
    private readonly Predicate<T> _canExecute; 
    private readonly Action<T> _execute; 

    public RelayCommand(Action<T> execute):this(obj => true, execute) 
    { 
    } 

    public RelayCommand(Predicate<T> canExecute, Action<T> execute) 
    { 
     _canExecute = canExecute; 
     _execute = execute; 
    } 

    public bool CanExecute(object parameter) 
    { 
     return _canExecute(parameter as T); 
    } 

    public void Execute(object parameter) 
    { 
     _execute(parameter as T); 
    } 

    public event EventHandler CanExecuteChanged; 
} 

Малый объяснение

  1. У нас есть три задания DataTemplate, журналы, по умолчанию.
  2. DataTemplateSelector будет управлять выбором шаблона данных на основе свойства Content ContentControl, к которому привязан DataTemplateSelector.
  3. Кнопка находится внутри сетки и привязана к команде из виртуальной машины родителя. Родительская виртуальная машина предоставляется в DataTemplate через объект Freezable proxie.

Дайте мне знать, если у вас есть проблемы с кодом.

С уважением.

3

Нечто подобное может работать: WPF MVVM navigate views

<Controls:MetroWindow ...> 
<Controls:MetroWindow.Resources> 
    <DataTemplate DataType="{x:Type my:LogViewModel}"> 
     <my:LogView/> 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type my:JobViewModel}"> 
     <my:JobView/> 
    </DataTemplate> 
</Controls:MetroWindow.Resources> 
<Grid> 
    <DockPanel> 
     <ContentControl Content="{Binding ViewModel}" /> 
    </DockPanel> 
</Grid> 

Затем написать ShowLogsCommand так, что он создает новый LogViewModel на основе выбранного задания, а затем устанавливает его в ViewModel собственности (в MainViewModel).
Удостоверьтесь, что вы правильно выполняете INotifyPropertyChanged.

Пример ShowLogsCommand (я не проверял это, используйте с осторожностью):

ICommand ShowLogsCommand => new RelayCommand(showLogsCommand); 

private void showLogsCommand(Job job) 
{ 
    ViewModel = new LogViewModel(job.JobId); 
} 

Измените XAML на:

<Button Content="Show Logs" 
     Command="{Binding ShowLogsCommand}" 
     CommandParameter="{Binding}" 
/> 
+0

Я пробовал это, но он терпит неудачу, можете ли вы привести пример примера реализации ShowLogsCommand(), пожалуйста, – Marzouk

+1

@Marzouk Обновлен мой ответ. –

+0

Я хочу объявить что кнопка «show logs» в JobUserControl (JobView), так что Command = «{Binding ShowLogsCommand}» будет определена в JobViewModel, и это моя главная проблема. – Marzouk

Смежные вопросы