1

Сделал на странице в WPF разбивку на несколько отдельных фреймов со страницами. Задача сделать по фрейму с оглавлением слева переход на разные страницы с текстом справа- уже в другом фрейме. Решил реализовать через и её параметр click с обработчиком событий в C#, но при попытке прописать в C# навигацию по страницам в другом фрейме компилятор не видит этого фрейма.введите сюда описание изображения

Код, задающий два фрейма:

<Frame Grid.Column="0" Grid.Row="0" x:Name="listframe"></Frame>
    <Frame Grid.Row="0" Grid.Column="1" x:Name="soderzhaniyeframe"></Frame>

Код, задающий гиперссылки и создающий обработчик события:

<Paragraph>
                1.<Hyperlink x:Name="Theme1hyper" Click="Theme1hyper_Click"> Первая тема</Hyperlink>
            </Paragraph>
            <Paragraph>
                <Hyperlink x:Name="Theme1_1pdhyper" Click="Theme1_1pdhyper_Click">1 Подтема</Hyperlink>
            </Paragraph>

Код события click на C#, который я пытаюсь применить, но компилятор не видит soderzhaniyeframe:

  private void Theme1hyper_Click(object sender, RoutedEventArgs e)
    {
        soderzhaniyeframe.navigation(new soderzhanie_page());
}

  • А вам принципиально фреймы нужны? – Aarnihauta Dec 01 '21 at 10:51
  • Если есть иное решение, которое будет работать по аналогии- готов применить и его. – Gr1n_DEE Dec 01 '21 at 10:59
  • У Ваших под тем могут быть еще под темы и так далее? – Aarnihauta Dec 01 '21 at 12:29
  • Под темы у под тем не не планируются – Gr1n_DEE Dec 01 '21 at 13:05
  • Вы фреймы используете чтобы правая часть (там где "описание" главы) имела возможность листать? Как книга в зависимости от главы меняет количество/контент страниц в правой части. – Aarnihauta Dec 02 '21 at 07:29
  • Да, каждая часть- самостоятельное окно, которое можно листать и менять при необходимости – Gr1n_DEE Dec 02 '21 at 07:57
  • А оглавление меняется при нажатии на кнопки сверху (оглавление, глоссарий итд..)? Или оглавление статично? – Aarnihauta Dec 02 '21 at 07:59
  • Дополните вопрос новой информацией о том, что вы хотите сделать, а что не получается. Сейчас не особо понятно в чем заключается проблема. Переформулируйте текст как минимум. – Aarnihauta Dec 02 '21 at 08:02
  • Я хочу сделать чтобы при нажатии на гиперссылку в одном фрейме в другом фрейме открылась нужная страница. Всё. – Gr1n_DEE Dec 02 '21 at 12:02

1 Answers1

0

Для начала стоит Вам прочитать про Binding в Wpf. Так как Wpf был нацелен непосредственно на MVVM, то и про него стоило бы прочитать и использовать данный подход. Работать с обработчиками событий у контролов неудобно, для этого и существует привязка (она же Binding)

Материала достаточно на YouTube (Павел Шмачилин, у него там часовые видео по WPF), есть Метанит, да и у Майкрософта есть хорошие примеры

Касаемо Вашей задачи, для начала создадим класс BaseViewModel и реализуем в ней INotifyPropertyChanged. Метод SetProperty позволит нам перенести часто используемый код в отдельный метод.

Теперь BaseViewModel.

class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = &quot;&quot;)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}

public void SetProperty&lt;T&gt;(ref T store, T value, [CallerMemberName] string prop = &quot;&quot;)
{
    if(store.Equals(value))
       return;

    store = value;
    OnPropertyChanged(prop);
}

}

Теперь нам нужен класс модели которая будет содержать в себе данные. Назовем её Theme, поскольку нам нужен список тем и подтем. В этом методе переопределю ToString() который будет возвращать Title (Это не самое удачное решение, потому что для вывода ToString() не используется, но мы опустим этот момент)

public class Theme
{
    public string Title { get; set; }
    public string Content { get; set; }
    public override string ToString()
    {
        return Title;
    }
}

Создадим ThemeViewModel которую будем привязывать ко всему окну.

class ThemeViewModel : BaseViewModel
{
    private string _content;
    private string _title;
//Эти свойства будем привязывать к конкретным элементам окна
public string Title { get =&gt; _title; set =&gt; SetProperty(ref _title, value); }
public string ThemeContent { get =&gt; _content; set =&gt; SetProperty(ref _content, value); }
public ObservableCollection&lt;Theme&gt; Themes { get; set; }

public ThemeViewModel()
{
    InitThemes();
}

private void InitThemes()
{
    //Это можно получить из БД к примеру, но я заполню вручную. 
    Themes = new ObservableCollection&lt;Theme&gt;();

    Theme theme = new Theme
    {
        Title = &quot;Title 1&quot;,
        Content = &quot;Content 1&quot;,
    };
    Theme theme2 = new Theme
    {
        Title = &quot;Title 2&quot;,
        Content = &quot;Content 2&quot;
    };

    Themes.Add(theme);
    Themes.Add(theme2);
}

}

Теперь окно xaml. Заметьте, что я добавил SelectionChanged у ListBox.

<Window x:Class="wpftest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:vm="clr-namespace:wpftest.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="500" Width="700">
&lt;!-- Обозначим контекст данных, из этого класса будут браться данные --&gt;
&lt;Window.DataContext&gt;
    &lt;vm:ThemeViewModel&gt;&lt;/vm:ThemeViewModel&gt;
&lt;/Window.DataContext&gt;

&lt;Grid.ColumnDefinitions&gt;
     &lt;ColumnDefinition Width=&quot;200&quot;&gt;&lt;/ColumnDefinition&gt;
     &lt;ColumnDefinition&gt;&lt;/ColumnDefinition&gt;
 &lt;/Grid.ColumnDefinitions&gt;

 &lt;Grid.RowDefinitions&gt;
      &lt;RowDefinition Height=&quot;45&quot;&gt;&lt;/RowDefinition&gt;
      &lt;RowDefinition Height=&quot;200&quot;&gt;&lt;/RowDefinition&gt;
      &lt;RowDefinition&gt;&lt;/RowDefinition&gt;
 &lt;/Grid.RowDefinitions&gt;


&lt;Grid Grid.Row=&quot;1&quot; Grid.Column=&quot;0&quot;&gt;
    &lt;ListBox x:Name=&quot;listBoxThemes&quot; ItemsSource=&quot;{Binding Themes}&quot; 
            SelectionChanged=&quot;listboxThemes_SelectionChanged&quot;&gt;
    &lt;/ListBox&gt;
&lt;/Grid&gt;

&lt;Grid Grid.Row=&quot;1&quot; Grid.Column=&quot;1&quot;&gt;
    &lt;ScrollViewer&gt;
            &lt;TextBlock Text=&quot;{Binding ThemeContent}&quot; FontSize=&quot;15&quot; TextWrapping=&quot;Wrap&quot;&gt;
            &lt;/TextBlock&gt;
    &lt;/ScrollViewer&gt;
&lt;/Grid&gt;

</Window>

Внутри MainWindow.cs:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
private void listboxThemes_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
    //Приводим текущий контекст данных к нашей ThemeViewModel и задаем свойство ThemeContent из SelectedItem нашего листбокса. Такое возможно, поскольку к листбоксу мы привязали ObservableCollection&lt;Theme&gt;
    (this.DataContext as ThemeViewModel).ThemeContent = (listBoxThemes.SelectedItem as Theme).Content;
}

}

Теперь запустим и вот что получим:

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

Aarnihauta
  • 2,326
  • 3
  • 12
  • 23
  • 1
    Если уж делаете по правилам MVVM, то не нарушайте их. В MVVM слой UI (View) должен быть максимально отвязан от приложения и не зависеть от данных, он должен лишь привязаться, не более. Другими словами, в MVVM не должно быть SelectionChanged="listboxThemes_SelectionChanged. Хотите по правилам, так привяжите выделенный объект и TextBlock к одному свойству, будет вам по правилам. А вообще, представленный вами вариант делается очень просто на самом XAML. Ну и последнее, как это отвечает на поставленный вопрос? Где тут гиперссылки, параграфы? – EvgeniyZ Dec 03 '21 at 17:04