【C#】5ちゃんねるビューアーを作ろう(WPF) その1
今回から数回に分けて、5ちゃんねるビューアーをWPF化してみようと思います。
今回はTreeView部分のみ作っていこうと思います。
また、以下の記事を前提に書いていきます。
heych.hatenablog.com
heych.hatenablog.com
<Window x:Class="_5chViewerWPF.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:local="clr-namespace:_5chViewerWPF" mc:Ignorable="d" Title="MainWindow" Height="450" Width="600"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="200"/> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid Grid.Row="0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="200"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TreeView Grid.Column="0" ItemsSource="{Binding Menus}"> <TreeView.Resources> <Style TargetType="TreeViewItem"> <Setter Property="IsExpanded" Value="{Binding IsExpanded}"/> </Style> </TreeView.Resources> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type local:TreeSource}" ItemsSource="{Binding Children}"> <TextBlock Text="{Binding Text}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> <GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch"/> </Grid> <GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch"/> </Grid> </Window>
- GridSplitter を使う事で、Gridの幅や高さを動的に変えることができる。
- TreeViewItemのIsExpandedプロパティをIsExpandedにバインドすることができる。
- TreeViewのItemTemplateにおいてHierarchicalDataTemplate を用いることで、ツリー構造のバインディングを実現できる。
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace _5chViewerWPF { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { ViewModel vm = new ViewModel(); public MainWindow() { InitializeComponent(); this.DataContext = vm; string html = ""; //まずHTMLの取得 HttpWebRequest req = WebRequest.CreateHttp("https://www2.5ch.net/5ch.html"); using (var res = req.GetResponse()) { using (var r = res.GetResponseStream()) { using (var sr = new StreamReader(r, Encoding.GetEncoding(932))) { html = sr.ReadToEnd(); } } } //Treeの作成 bool stTag = false; bool edTag = false; bool isAttr = false; string currentTag = null; string currentAttr = null; string tmp = null; Regex regex = new Regex("(?:href=\")(.*)(?:\")"); for (int i = 0; i < html.Length; i++) { if (html[i] == '<') { if (html[i + 1] == '/') { if (tmp != null) { if (currentTag == "B") { TreeSource tn = new TreeSource(tmp); vm.Menus.Add(tn); } else if (currentTag == "a" && vm.Menus.Count > 0) { string href = "https:" + regex.Match(currentAttr).Groups[1].Value + "subback.html"; TreeSource tn = new TreeSource(tmp); tn.URL = href; vm.Menus[vm.Menus.Count - 1].Children.Add(tn); } } edTag = true; } else { stTag = true; tmp = null; currentTag = null; currentAttr = null; } } else if (html[i] == '>') { if (edTag) currentTag = null; stTag = false; edTag = false; isAttr = false; } else if (html[i] == ' ' && stTag) { isAttr = true; } else { if (isAttr) currentAttr += html[i]; else if (stTag) currentTag += html[i]; else if (!edTag && (currentTag == "B" || currentTag == "a")) { tmp += html[i]; if (tmp == "他のサイト") break; } } } } } public class ViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } ObservableCollection<TreeSource> _Menus = new ObservableCollection<TreeSource>(); public ObservableCollection<TreeSource> Menus { get { return _Menus; } set { _Menus = value; OnPropertyChanged("Menus"); } } } public class TreeSource { public TreeSource Parent { get; set; } public ObservableCollection<TreeSource> Children { get; set; } public bool IsExpanded { get; set; } public string Text { get; set; } public string URL { get; set; } public TreeSource(string text) { Text = text; Children = new ObservableCollection<TreeSource>(); } } }