【C#】WPFって何よ
Windows Presentation Foundation (WPF) は、デスクトップ クライアント アプリケーションを作成する UI フレームワークです。
WPF の開発プラットフォームは、アプリケーション モデル、リソース、コントロール、グラフィックス、レイアウト、データ バインディング、ドキュメント、セキュリティなどのさまざまなアプリケーション開発機能の一式をサポートします。
(コピペ)
よく使う機能を簡単にまとめると、好きなようにレイアウトし、データバインディングでいい感じに描画できるのがWPFって感じです。
なんていうんですかね、TreeViewにTreeViewItemってあったじゃないですか、あれのItemをXAMLっていうので定義できるので、本当に自分の好きなようにレイアウトできるんですよね。(もちろん素のままでもOKです。)
なので、せっかくこんな便利な機能があるので、使わなきゃ損という事で、今回からWPFを勉強していこうと思います。
ストップウォッチをWPF化してみよう
以下の記事で書いたストップウォッチをWPF化してみようと思います。
heych.hatenablog.com
<Window x:Class="StopwatcchWPF.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:StopwatcchWPF" mc:Ignorable="d" Title="MainWindow" Height="300" Width="300"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="50"/> <RowDefinition Height="30"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Label Grid.Row="0" Content="{Binding Time}" FontSize="24"/> <Grid Grid.Row="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Button Grid.Column="0" x:Name="button1" Content="Start"/> <Button Grid.Column="1" x:Name="button2" Content="Stop"/> <Button Grid.Column="2" x:Name="button3" Content="LAP"/> </Grid> <DataGrid Grid.Row="2" ItemsSource="{Binding LAPs}" AutoGenerateColumns="False" CanUserAddRows="False"> <DataGrid.Columns> <DataGridTextColumn Header="時刻" Binding="{Binding Time}" IsReadOnly="True"/> <DataGridTextColumn Header="ラップタイム" Binding="{Binding LAPTime}" IsReadOnly="True"/> </DataGrid.Columns> </DataGrid> </Grid> </Window>
- DataGridは AutoGenerateColumns="False"に設定しないと自動で列を作ってしまう。
- また、DataGridを読取専用にしたい場合、列に対し、IsReadOnly="True"を設定する必要がある
- Bindingの部分は自分で決める必要がある
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; 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 StopwatcchWPF { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { ViewModel vm; DateTime now; DateTime startTime; BackgroundWorker bw; TimeSpan ts; List<TimeSpan> tsl = new List<TimeSpan>(); enum StopwatchMode { Time, Stopwatch, Pause } StopwatchMode mode = StopwatchMode.Time; public MainWindow() { InitializeComponent(); vm = new ViewModel(); this.DataContext = vm; ts = new TimeSpan(0); bw = new BackgroundWorker(); bw.DoWork += Bw_DoWork; bw.RunWorkerAsync(); } private void Bw_DoWork(object sender, DoWorkEventArgs e) { while (true) { now = DateTime.Now; if (mode == StopwatchMode.Stopwatch) { ts = now - startTime; try { vm.Time = String.Format("{0:D2}:{1:D2}:{2:D2}.{3:D3}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds); } catch (Exception ex) { } } else if (mode == StopwatchMode.Time) { try { vm.Time = String.Format("{0:D2}:{1:D2}:{2:D2}", now.Hour, now.Minute, now.Second, now.Millisecond); } catch (Exception ex) { } } else if (mode == StopwatchMode.Pause) { //何もしない } } } private void button1_Click(object sender, RoutedEventArgs e) { if ((string)button1.Content == "Start") { if (mode != StopwatchMode.Pause) { startTime = DateTime.Now; } else if (mode == StopwatchMode.Pause) { startTime = DateTime.Now - ts; } mode = StopwatchMode.Stopwatch; button1.Content = "Pause"; } else { mode = StopwatchMode.Pause; button1.Content = "Start"; } } private void button2_Click(object sender, RoutedEventArgs e) { mode = StopwatchMode.Time; button1.Content = "Start"; } private void button3_Click(object sender, RoutedEventArgs e) { if (mode != StopwatchMode.Stopwatch) return; tsl.Add(ts); TimeSpan tmp = new TimeSpan(0); if (tsl.Count <= 1) { tmp = tsl[tsl.Count - 1]; } else { tmp = tsl[tsl.Count - 1] - tsl[tsl.Count - 2]; } var lap = new LAP(); lap.Time = String.Format("{0:D2}:{1:D2}:{2:D2}.{3:D3}", now.Hour, now.Minute, now.Second, now.Millisecond); lap.LAPTime = String.Format("{0:D2}:{1:D2}:{2:D2}.{3:D3}", tmp.Hours, tmp.Minutes, tmp.Seconds, tmp.Milliseconds); vm.LAPs.Add(lap); } } public class ViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } string _Time = "00:00:00.000"; public string Time { get { return _Time; } set { _Time = value; RaisePropertyChanged("Time"); } } ObservableCollection<LAP> _LAPs = new ObservableCollection<LAP>(); public ObservableCollection<LAP> LAPs { get { return _LAPs; } set { _LAPs = value; RaisePropertyChanged("LAPs"); } } } public class LAP { public string Time { get; set; } public string LAPTime { get; set; } } }
- ラベルの更新や、DataGridの更新には「PropertyChanged」イベントで通知する必要がある。
- List
はAddしただけでは更新が通知されないため、「ObservableCollection」を使う必要がある。