Обработчик события Click гиперссылки не видит фрейм
Сделал на странице в 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());
}
Ответы (1 шт):
Для начала стоит Вам прочитать про Binding в Wpf. Так как Wpf был нацелен непосредственно на MVVM, то и про него стоило бы прочитать и использовать данный подход. Работать с обработчиками событий у контролов неудобно, для этого и существует привязка (она же Binding)
Материала достаточно на YouTube (Павел Шмачилин, у него там часовые видео по WPF), есть Метанит, да и у Майкрософта есть хорошие примеры
Касаемо Вашей задачи, для начала создадим класс BaseViewModel и реализуем в ней INotifyPropertyChanged. Метод SetProperty позволит нам перенести часто используемый код в отдельный метод.
Теперь BaseViewModel.
class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
public void SetProperty<T>(ref T store, T value, [CallerMemberName] string prop = "")
{
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 => _title; set => SetProperty(ref _title, value); }
public string ThemeContent { get => _content; set => SetProperty(ref _content, value); }
public ObservableCollection<Theme> Themes { get; set; }
public ThemeViewModel()
{
InitThemes();
}
private void InitThemes()
{
//Это можно получить из БД к примеру, но я заполню вручную.
Themes = new ObservableCollection<Theme>();
Theme theme = new Theme
{
Title = "Title 1",
Content = "Content 1",
};
Theme theme2 = new Theme
{
Title = "Title 2",
Content = "Content 2"
};
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">
<!-- Обозначим контекст данных, из этого класса будут браться данные -->
<Window.DataContext>
<vm:ThemeViewModel></vm:ThemeViewModel>
</Window.DataContext>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="45"></RowDefinition>
<RowDefinition Height="200"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="1" Grid.Column="0">
<ListBox x:Name="listBoxThemes" ItemsSource="{Binding Themes}"
SelectionChanged="listboxThemes_SelectionChanged">
</ListBox>
</Grid>
<Grid Grid.Row="1" Grid.Column="1">
<ScrollViewer>
<TextBlock Text="{Binding ThemeContent}" FontSize="15" TextWrapping="Wrap">
</TextBlock>
</ScrollViewer>
</Grid>
</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<Theme>
(this.DataContext as ThemeViewModel).ThemeContent = (listBoxThemes.SelectedItem as Theme).Content;
}
}
Теперь запустим и вот что получим:
