0

Используется Master-Detail:

  • Master-List;
  • Detail-UserControl.

Я пробую использовать ListBox. А у него проблемы с командами...

Вопросы.
1. Как ListBox применить для работы с командами?

или

2. Что удобнее использовать взамен ListBox для отображения списка и передачи Item в UserControl? Чтобы можно было просто список отобразить или(и) список с кнопками, иконками...


https://github.com/jhon65496/MasterDetailApp01

Pic-1
введите сюда описание изображения

Pic-2
введите сюда описание изображения


RelayCommand.cs

public class RelayCommand : ICommand
{
        private Action<object> execute;
        private Func<object, bool> canExecute;
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public RelayCommand(Action&lt;object&gt; execute, Func&lt;object, bool&gt; canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

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

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

}

Базовый ViewModel.cs

internal abstract class ViewModel : INotifyPropertyChanged, IDisposable
{
            public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string PropertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));            
        }

        protected virtual bool Set&lt;T&gt;(ref T field, T value, [CallerMemberName] string PropertyName = null)
        {
            if (Equals(field, value)) return false;
            field = value;
            OnPropertyChanged(PropertyName);
            return true;
        }
        public void Dispose()
        {
            Dispose(true);
        }

        private bool _Disposed;
        protected virtual void Dispose(bool Disposing)
        {
            if(!Disposing || _Disposed) return;
            _Disposed = true;
            // Освобождение управляемых ресурсов
        }

}


MainWindow

View

MainWindow.xaml

<Window x:Class="GroupsStudents01.Views.Windows.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" mc:Ignorable="d"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local=&quot;clr-namespace:GroupsStudents01.Views.Windows&quot;
    xmlns:vm =&quot;clr-namespace:GroupsStudents01.ViewModels&quot;

    Title=&quot;{Binding Title}&quot;

    Height=&quot;450&quot; Width=&quot;800&quot;&gt;

&lt;Window.DataContext&gt;
    &lt;vm:MainWindowViewModel/&gt;
&lt;/Window.DataContext&gt;
&lt;DockPanel&gt;        
            &lt;Grid&gt;
                &lt;Grid.ColumnDefinitions&gt;
                    &lt;!--&lt;ColumnDefinition Width=&quot;3*&quot;/&gt;
                    &lt;ColumnDefinition Width=&quot;7*&quot;/&gt;--&gt;
                    &lt;ColumnDefinition Width=&quot;250&quot;/&gt;
                    &lt;ColumnDefinition/&gt;
                &lt;/Grid.ColumnDefinitions&gt;
                &lt;GroupBox Grid.Column=&quot;0&quot; Header=&quot;Группы&quot;&gt;
                    &lt;ListBox ItemsSource=&quot;{Binding Groups}&quot; 
                             DisplayMemberPath=&quot;Name&quot;
                             SelectedItem=&quot;{Binding SelectedGroup, Mode=TwoWay}&quot;
                             SelectedIndex=&quot;10&quot;/&gt;
                &lt;/GroupBox&gt;
                &lt;GroupBox Grid.Column=&quot;1&quot; Header=&quot;Группа. Детали&quot;&gt;
            &lt;ContentControl Content=&quot;{Binding CurrentModel}&quot;/&gt;
        &lt;/GroupBox&gt;
            &lt;/Grid&gt;
&lt;/DockPanel&gt;

</Window>

ViewModel

MainWindowViewModel.cs

internal class MainWindowViewModel : ViewModel
    {
        public ObservableCollection<Group> Groups { get; }
    #region SelectedGroup
    private Group _SelectedGroup;

    public Group SelectedGroup
    {
        get =&gt; _SelectedGroup;
        set =&gt; Set(ref _SelectedGroup, value);        
    }
    #endregion

    #region Заголовок окна. Для теста подключения View + ViewModel
    private string _Title = &quot;Группы-Студенты&quot;;

    /// &lt;summary&gt;Заголовок окна&lt;/summary&gt;
    public string Title
    {
        get =&gt; _Title;            
        set =&gt; Set(ref _Title, value);
    }

    #endregion

    #region CurrentModel : ViewModel - Текущая дочерняя модель-представления

    /// &lt;summary&gt;Текущая дочерняя модель-представления&lt;/summary&gt;
    private ViewModel _CurrentModel;

    /// &lt;summary&gt;Текущая дочерняя модель-представления&lt;/summary&gt;
    public ViewModel CurrentModel { get =&gt; _CurrentModel; private set =&gt; Set(ref _CurrentModel, value); }

    #endregion

    #region Command ShowGroupDetailViewCommand - Отобразить представление группы
    private RelayCommand showGroupDetailViewCommand;
    public RelayCommand ShowGroupDetailViewCommand
    {
        get
        {
            return showGroupDetailViewCommand ??
              (showGroupDetailViewCommand = new RelayCommand(obj =&gt;
              {
                  // Group group = obj as _SelectedGroup;
                  CurrentModel = new GroupDetailViewModel(SelectedGroup);
              }));
        }
    }
    #endregion

    /* ---------------------------------- */
    #region Конструктор        
    public MainWindowViewModel()
    {
        var groups = Enumerable.Range(1, 20).Select(i =&gt; new Group
        {
            Id = i,
            Name = $&quot;Группа {i}&quot;,
            Description = $&quot;Description {i}&quot;,
        });

        Groups = new ObservableCollection&lt;Group&gt;(groups);        
    }
    #endregion
    /* ---------------------------------- */

}


GroupDetail

View

GroupDetailView.xaml

<UserControl x:Class="GroupsStudents01.Views.GroupDetailView"
             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" 
             mc:Ignorable="d" 
             xmlns:local="clr-namespace:GroupsStudents01.Views"             
             xmlns:vm="clr-namespace:GroupsStudents01.ViewModels"             
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.DataContext>
        <vm:GroupDetailViewModel/>        
    </UserControl.DataContext>
    <Grid>
        <GroupBox Grid.Column="2" Header="Информация" DataContext="{Binding Group}">
            <DockPanel>
                <UniformGrid DockPanel.Dock="Bottom" Rows="1" Margin="-3,3">
                    <Button Foreground="Green" Padding="0,5" Margin="3,0"
            Command="{Binding CreateNewStudentCommand}"
            CommandParameter="{Binding SelectedGroup}"/>
                    <Button Foreground="Blue" Padding="0,5" Margin="3,0"
            Command="{Binding EditStudentCommand}"
            CommandParameter="{Binding SelectedStudent}"/>
                </UniformGrid>
                <Grid Margin="3" DataContext="{Binding Book}">
                    <Grid.Resources>
                        <Style TargetType="TextBlock">
                            <Style.Triggers>
                                <Trigger Property="Grid.Column" Value="0">
                                    <Setter Property="HorizontalAlignment" Value="Right"/>
                                    <Setter Property="Margin" Value="0,0,2,0"/>
                                    <Setter Property="FontWeight" Value="Bold"/>
                                </Trigger>
                                <Trigger Property="Grid.Column" Value="1">
                                    <Setter Property="Margin" Value="2,0,0,0"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </Grid.Resources>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                &lt;TextBlock Grid.Column=&quot;0&quot; Grid.Row=&quot;0&quot; Text=&quot;Id:&quot;/&gt;
                &lt;TextBlock Grid.Column=&quot;0&quot; Grid.Row=&quot;1&quot; Text=&quot;Category:&quot;/&gt;
                &lt;TextBlock Grid.Column=&quot;0&quot; Grid.Row=&quot;2&quot; Text=&quot;Name:&quot;/&gt;
                &lt;TextBlock Grid.Column=&quot;0&quot; Grid.Row=&quot;3&quot; Text=&quot;Description:&quot;/&gt;
                &lt;TextBlock Grid.Column=&quot;0&quot; Grid.Row=&quot;4&quot; Text=&quot;Дата рождения:&quot;/&gt;

                &lt;TextBlock Grid.Column=&quot;1&quot; Grid.Row=&quot;0&quot; Text=&quot;{Binding Id}&quot;/&gt;
                &lt;TextBlock Grid.Column=&quot;1&quot; Grid.Row=&quot;2&quot; Text=&quot;{Binding Name}&quot;/&gt;
                &lt;TextBlock Grid.Column=&quot;1&quot; Grid.Row=&quot;3&quot; Text=&quot;{Binding Description}&quot;/&gt;                   

            &lt;/Grid&gt;
        &lt;/DockPanel&gt;
    &lt;/GroupBox&gt;
&lt;/Grid&gt;

</UserControl>

GroupDetailViewModel.cs

 internal class GroupDetailViewModel : ViewModel
    {
        #region Group
        private Group _group;
    public Group Group
    {
        get =&gt; _group;
        set =&gt; Set(ref _group, value);
    }
    #endregion

    public GroupDetailViewModel(Group group)
    {
        _group = group;
    }
}


Model

Group.cs

internal class Group 
{
        public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }

}


Обновление-1

Под Master-Detail понимаю следующую компоновку.(см. картинку)

введите сюда описание изображения

Пример: https://ru.stackoverflow.com/a/816149/220553
В примере отображение Detail осуществляется в StackPanel (если я правильно понял).

<StackPanel Orientation="Horizontal" Margin="5,0">
    <TextBlock Text="{Binding Messages/Autor}"/>
    <TextBlock Text="{Binding Messages/DateTime}" Margin="5,0,0,0"/>
</StackPanel>

Моя цель отобразить Detail в отдельном UserControl, который вписан в MainWindow.xaml. Пример моей попытки реализовать решение...

 <GroupBox Grid.Column="1" Header="Группа. Детали">
                <ContentControl Content="{Binding CurrentModel}"/>
</GroupBox>


Обновление-2

Решение-1. Полуработает.

Проект с изменениями. github.com--MasterDetailApp01

введите сюда описание изображения

Выполнено

Создал словарь ModelMainView.xaml
введите сюда описание изображения

<DataTemplate DataType="{x:Type vm:GroupDetailViewModel}">
    <view:GroupDetailView DataContext="{Binding Group}"/>
</DataTemplate> 

Прописал словарь ModelMainView.xaml в App.xaml

<Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Templates/ModelMainView.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>         
</Application.Resources>

MainWindowViewModel.cs Создал метод

private void OnShowGroupDetailViewCommandExecuted()
{   
  CurrentModel = new GroupDetailViewModel(SelectedGroup);
}

Добавил метод OnShowGroupDetailViewCommandExecuted() в свойство SelectedGroup

#region SelectedGroup
private Group _SelectedGroup;

public Group SelectedGroup { get => _SelectedGroup; set { Set(ref _SelectedGroup, value); OnShowGroupDetailViewCommandExecuted(); }

} #endregion

Создал команду ShowGroupDetailViewCommand2на случай вызова нового представления кнопкой.

private RelayCommand showGroupDetailViewCommand2;
public RelayCommand ShowGroupDetailViewCommand2
{
    get
    {
        return showGroupDetailViewCommand2 ??
        (showGroupDetailViewCommand2 = new RelayCommand(obj =>
        {
            OnShowGroupDetailViewCommandExecuted();
        }));
    }
}

Примечания

В словаре ModelMainView.xaml

  1. Если изменить DataContextкак показано ниже, GroupDetailView не видит GroupDetailViewModel
<DataTemplate DataType="{x:Type vm:GroupDetailViewModel}">
    <view:GroupDetailView DataContext="{Binding}"/>
</DataTemplate> 
  1. Если изменить DataContextкак показано ниже, GroupDetailView не видит GroupDetailViewModel
<DataTemplate DataType="{x:Type vm:GroupDetailViewModel}">
    <view:GroupDetailView/>
</DataTemplate> 

введите сюда описание изображения


Вопрос

  1. Как сделать чтобы GroupDetailView видел GroupDetailViewModel?
    Чтобы там(в GroupDetailViewModel) можно было размещать команды с параметрами и т.д.
    Напомню, что сейчас GroupDetailView видит только свойство Group

Т.е. я ожидаю сделать в ModelMainView.xaml

<DataTemplate DataType="{x:Type vm:GroupDetailViewModel}">
    <view:GroupDetailView/>
</DataTemplate> 

Сейчас в ModelMainView.xaml DataContext выглядит DataContext="{Binding Group}"
А я в примерах вижу

  • <view:GroupDetailView DataContext="{Binding}"/>
    или
  • <view:GroupDetailView/>
    В моём случае DataContext из примеров не приводит к нужному результату.

Потом в GroupDetailView.xaml
В GroupBox пропишу DataContext типа

<GroupBox Grid.Column="2" Header="Информация" DataContext="{Binding Grouop}">

А дальше в дочерних полях прибиндю свойства Группы.


Обновление-3

https://github.com/jhon65496/MasterDetailApp01/tree/answ02

GroupDetailViewModel.cs

internal class GroupDetailViewModel : ViewModel
{
    private int id;
public int Id
{
    get { return id; }
    set =&gt; Set(ref id, value);
}


private string name;

public string Name
{
    get { return name; }
    set =&gt; Set(ref name, value);
}


private string description;

public string Description
{
    get { return description; }
    set =&gt; Set(ref description, value);
}


public GroupDetailViewModel()
{

}

}

MainWindowViewModel.cs

public ObservableCollection<GroupDetailViewModel> Groups { get; set; }

public IEnumerable<GroupDetailViewModel> LoadData2() { var groups = Enumerable.Range(1, 20).Select( i => new GroupDetailViewModel { Id = i, Name = $"Группа {i}", Description = $"Description {i}" });

return groups;    

}

#region Конструктор
public MainWindowViewModel() {
Groups = new ObservableCollection<GroupDetailViewModel>(LoadData2()); } #endregion

#region SelectedGroup2 private GroupDetailViewModel _SelectedGroup2;

public GroupDetailViewModel SelectedGroup2 { get => _SelectedGroup2; set { Set(ref _SelectedGroup2, value);

    OnShowGroupDetailViewCommandExecuted2(value);
}

} #endregion

private void OnShowGroupDetailViewCommandExecuted2(GroupDetailViewModel selectedGroup) { CurrentModel = selectedGroup; }

ModelMainView.xaml

<DataTemplate DataType="{x:Type vm:GroupDetailViewModel}">
   <view:GroupDetailView/>
</DataTemplate> 

Вопрос

В GroupDetailView.xaml не работает биндинг.
В чём проблема?


Обновление-4

По материалам Обновление-4.
https://github.com/jhon65496/MasterDetailApp01/tree/answ02
В GroupDetailView.xaml - <GroupBox> - удалено: DataContext="{Binding Grouop}"
Решение работает.

Картинка-1

введите сюда описание изображения

Но не смотря на то, что приложение запускается GroupDetailView.xaml в конструкторе выдаёт ошибки биндинга.

Вопрос.

  1. Почему в GroupDetailView.xaml в конструкторе ошибки биндинга?
  2. Код выполнен правильно?

Примечание.
Я закрывал/открывал GroupDetailView.xaml, Решение, VisualStudio.

введите сюда описание изображения

eusataf
  • 249
  • 1
  • 8
  • Забыли метку c#. Также много лишнего написали, к примеру, что за Master-Detail? По вопросу: Всякие <Window.DataContext> - плохо. "Передать данные в контрол" - делайте DependencyProperty и через него устанавливайте все, что хотите. "Как вывести подробности" - при помощи синхронизации. "что я делаю не так?" - устанавливаете лишние DataContext, а также я не вижу, где используется GroupDetailView, ибо я так понимаю это вид контента, ок, тогда где DataTemplate с указанием типа и вида? – EvgeniyZ Apr 22 '23 at 23:16
  • @EvgeniyZ 1. что за **Master-Detail**? см. Обновление-1. 2. Остальную представленную вами информацию перевариваю... Буду признателен, если продемонстрируете решение на моём или другом более правильном примере... Если у вас есть возможность... – eusataf Apr 23 '23 at 06:51
  • который вписан в MainWindow.xaml - я не вижу этого, где хоть одно использование данного контрола? осуществляется в StackPanel - без разницы где вы будете это использовать. Ну вот допустим самое простое: 1. Убираем <UserControl.DataContext>. 2. Задаем вид контенту в ресурсах окна/контрола/приложения, получаем примерно такое. 3. Устанавливаем ListBox свойство IsSynchronizedWithCurrentItem. 4. Задаем контенту тоже самое свойство, что и ListBox, но со слешем в конце (Content="{Binding Groups/}"). Все, вам автоматом WPF проставит все нужное. – EvgeniyZ Apr 23 '23 at 10:07
  • Другое вариант (как по мне более правильный) - отказаться от ContentControl и использовать контрол напрямую. Тут либо 1. Указывать ему DataContext (<local:GroupDetailView DataContext = "{Binding Groups/}"/>) 2. Задать ему DependencyProperty и заполнять их. Сами DP делаются просто, пишем в студии propdpи жмем TAB, правим немного XAML, готово (как пример). Ну а использование тогда будет такое <local:GroupDetailView GroupId = "{Binding Groups/Id}" GroupName = "{Binding Groups/Name}" />, ну и так каждое нужное свойство. – EvgeniyZ Apr 23 '23 at 10:12
  • Я вам уже все нужное дал, чего вы еще от меня хотите?) Даже пошаговую инструкцию дал одного из вариантов, дал ссылки на подробные ответы, где я подробно говорил о конкретном аспекте. Изучайте, возникнут вопросы, пишите. – EvgeniyZ Apr 23 '23 at 10:56
  • @EvgeniyZ Извините, можно ещё чуток подсказку? 1. См. Обновление-2 2. Как сделать чтобы GroupDetailView видел GroupDetailViewModel? Чтобы там(в GroupDetailViewModel) можно было размещать команды с параметрами – eusataf Apr 24 '23 at 10:33
  • Что-то я не заметил сразу то, что вы выводите на экран модель (Group), а не как положено ViewModel. Когда вы устанавливаете через DataTemplate нужный вид объекту, за вас WPF автоматически задает DataContext, вам не надо его прописывать вручную. Контекстом будет являться сам объект. Что касается GroupDetailViewModel, то он у вас должен быть изначально в выводимой коллекции ObservableCollection<GroupDetailViewModel> Groups, ибо это по сути VM класс Group (Model). Модель очень редко напрямую выводят на экран, обычно получают модель, и на ее основе создают VM, которая и выводится. – EvgeniyZ Apr 24 '23 at 11:43
  • @EvgeniyZ 1. Я правильно вас понял? (см. Обновление-3). У меня чего-то не работает. ЮзерКонтрол в деталях отображает, а данных в ЮзерКонтроле нет.. 2. В GroupDetailView.xaml не работает биндинг. В чём проблема? 3. Сам код правильный? – eusataf Apr 24 '23 at 14:32
  • "В GroupDetailView.xaml не работает биндинг" - наверно потому, что вы не услышали про то, что "вам контекст ставит сам WPF", а вы игнорируя это делаете DataContext="{Binding Grouop}"? – EvgeniyZ Apr 24 '23 at 14:41
  • @EvgeniyZ 1. Виноват. Исправил. Работает. см. Обновление-4. 2. Но в конструкторе в GroupDetailView.xaml всё равно ошибка биндинга... Но приложение запускается.. – eusataf Apr 24 '23 at 15:53
  • Это не ошибка биндинга, а просто так отображает студия данные, о которых ничего не знает. Подобное не влияет на проект. Если всеж хотите, чтоб студия знала о свойствах, предлагала вам их, и так далее, то согласитесь с тем, что на скрине, она установит d:DataContext - это контекст только для дизайнера, не для программы. А вообще, советую запустить проект, и работать в живую, не смотря на то, что в конструкторе. – EvgeniyZ Apr 24 '23 at 16:03

0 Answers0