C# WPF-株価表示アプリ-02
前回までのあらすじ
完成図
View
構成
App.xaml
<Application x:Class="StockData.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:StockData" Startup="StartUp_App" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d1p1:Ignorable="d" xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006"> <Application.Resources> </Application.Resources> </Application>
スタートメソッドを変更するために下記を設定
Startup="StartUp_App"
詳しい説明は下記となります。
C# WPF-Tips-起動メソッドの変更 - エンジニアの備忘録
App.xaml.cs
namespace StockData { public partial class App : Application { public void StartUp_App(object sender, StartupEventArgs e) { StockDataViewModel stockDataViewModel = new StockDataViewModel(); StockDataView stockDataView = new StockDataView(stockDataViewModel) { DataContext = stockDataViewModel }; stockDataView.Show(); } } }
StockDataView.xaml
メインXML
長いですが、各々説明します。
<Window x:Class="StockData.View.StockDataView" 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:vali="clr-namespace:StockData.Validation" xmlns:local="clr-namespace:StockData" mc:Ignorable="d" Title="StockData" Height="400" Width="410" Icon="/img/icon.png"> <StackPanel Orientation="Vertical" HorizontalAlignment="Center"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="5"> <Label Content="Start : "/> <TextBox Width="70" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"> <TextBox.Text> <Binding Path="Condition.StartDate" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" StringFormat="yyyy/M/d"> <Binding.ValidationRules> <vali:DateValidationRule/> </Binding.ValidationRules> </Binding> </TextBox.Text> <TextBox.Style> <Style TargetType="TextBox"> <Style.Triggers> <Trigger Property="Validation.HasError" Value="true"> <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/> <Setter Property="Background" Value="#ffeeff"/> </Trigger> </Style.Triggers> </Style> </TextBox.Style> <TextBox.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding FilterCommand}"/> </TextBox.InputBindings> </TextBox> <Label Content=" ~ End : "/> <TextBox Width="70" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"> <TextBox.Text> <Binding Path="Condition.EndDate" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" StringFormat="yyyy/M/d"> <Binding.ValidationRules> <vali:DateValidationRule/> </Binding.ValidationRules> </Binding> </TextBox.Text> <TextBox.Style> <Style TargetType="TextBox"> <Style.Triggers> <Trigger Property="Validation.HasError" Value="true"> <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/> <Setter Property="Background" Value="#ffeeff"/> </Trigger> </Style.Triggers> </Style> </TextBox.Style> <TextBox.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding FilterCommand}"/> </TextBox.InputBindings> </TextBox> <Button Content="ON" Command="{Binding FilterCommand}" Margin="20, 0, 0, 0"/> <Button Content="Chart" Command="{Binding OpenChartCommand}" Margin="20, 0, 0, 0"/> </StackPanel> <StackPanel Orientation="Horizontal" Margin="5"> <DataGrid x:Name="StockGrid" ItemsSource="{Binding StockList}" AutoGenerateColumns="False" HeadersVisibility="Column" MaxHeight="300" CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeColumns="False" CanUserResizeRows="False"> <DataGrid.ColumnHeaderStyle> <Style TargetType="DataGridColumnHeader"> <Setter Property="HorizontalContentAlignment" Value="Center"/> </Style> </DataGrid.ColumnHeaderStyle> <DataGrid.Columns> <DataGridTextColumn Header="Date" Binding="{Binding Date, StringFormat='yyyy/MM/dd'}" Width="80" IsReadOnly="True"> <DataGridTextColumn.ElementStyle> <Style TargetType="TextBlock"> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </DataGridTextColumn.ElementStyle> </DataGridTextColumn> <DataGridTextColumn Header="Open" Binding="{Binding Open, StringFormat=F2}" Width="70" IsReadOnly="True"> <DataGridTextColumn.ElementStyle> <Style TargetType="TextBlock"> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </DataGridTextColumn.ElementStyle> </DataGridTextColumn> <DataGridTextColumn Header="Close" Binding="{Binding Close, StringFormat=F2}" Width="70" IsReadOnly="True"> <DataGridTextColumn.ElementStyle> <Style TargetType="TextBlock"> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </DataGridTextColumn.ElementStyle> </DataGridTextColumn> <DataGridTextColumn Header="High" Binding="{Binding High, StringFormat=F2}" Width="70" IsReadOnly="True"> <DataGridTextColumn.ElementStyle> <Style TargetType="TextBlock"> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </DataGridTextColumn.ElementStyle> </DataGridTextColumn> <DataGridTextColumn Header="Low" Binding="{Binding Low, StringFormat=F2}" Width="70" IsReadOnly="True"> <DataGridTextColumn.ElementStyle> <Style TargetType="TextBlock"> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </DataGridTextColumn.ElementStyle> </DataGridTextColumn> </DataGrid.Columns> </DataGrid> </StackPanel> </StackPanel> </Window>
アイコンの設定
Icon="/img/icon.png"
テキストボックスの設定
下記は"StartDate"、"EndDate"もほぼ同じ
<TextBox Width="70" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"> <TextBox.Text> <Binding Path="Condition.StartDate" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" StringFormat="yyyy/M/d"> <Binding.ValidationRules> <vali:DateValidationRule/> </Binding.ValidationRules> </Binding> </TextBox.Text> <TextBox.Style> <Style TargetType="TextBox"> <Style.Triggers> <Trigger Property="Validation.HasError" Value="true"> <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/> <Setter Property="Background" Value="#ffeeff"/> </Trigger> </Style.Triggers> </Style> </TextBox.Style> <TextBox.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding FilterCommand}"/> </TextBox.InputBindings> </TextBox>
1.TextBox.Text
TextBoxに表示する文字をBindingさせる
2.UpdateSourceTrigger
変更通知を送るタイミングの設定
PropertyChanged:プロパティが変更されたタイミング
LostFocus:TextBoxからフォーカスが外れたタイミング
3.Mode
通知の方向性の設定
TwoWay:View→Model、Model→Viewの両方向で通知
4.StringFormat
Textのフォーマット
5.ValidationRules
独自のValidationルールの設定
<Binding.ValidationRules> <vali:DateValidationRule/> </Binding.ValidationRules>
上記を使うために下記の設定が必要
xmlns:vali="clr-namespace:StockData.Validation"
6.Validationエラー内容をToolTipに表示
<TextBox.Style> <Style TargetType="TextBox"> <Style.Triggers> <Trigger Property="Validation.HasError" Value="true"> <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/> <Setter Property="Background" Value="#ffeeff"/> </Trigger> </Style.Triggers> </Style> </TextBox.Style>
7.EnterでCommand実行
<TextBox.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding FilterCommand}"/> </TextBox.InputBindings>
ボタンの設定
<Button Content="ON" Command="{Binding FilterCommand}" Margin="20, 0, 0, 0"/>
1.Content
ボタンに表示する文字列
2.Command
ボタンを押した時のCommand
DataGridの設定
<DataGrid x:Name="StockGrid" ItemsSource="{Binding StockList}" AutoGenerateColumns="False" HeadersVisibility="Column" MaxHeight="300" CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeColumns="False" CanUserResizeRows="False"> <DataGrid.ColumnHeaderStyle> <Style TargetType="DataGridColumnHeader"> <Setter Property="HorizontalContentAlignment" Value="Center"/> </Style> </DataGrid.ColumnHeaderStyle> <DataGrid.Columns> <DataGridTextColumn Header="Date" Binding="{Binding Date, StringFormat='yyyy/MM/dd'}" Width="80" IsReadOnly="True"> <DataGridTextColumn.ElementStyle> <Style TargetType="TextBlock"> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </DataGridTextColumn.ElementStyle> </DataGridTextColumn> <DataGridTextColumn Header="Open" Binding="{Binding Open, StringFormat=F2}" Width="70" IsReadOnly="True"> <DataGridTextColumn.ElementStyle> <Style TargetType="TextBlock"> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </DataGridTextColumn.ElementStyle> </DataGridTextColumn> <DataGridTextColumn Header="Close" Binding="{Binding Close, StringFormat=F2}" Width="70" IsReadOnly="True"> <DataGridTextColumn.ElementStyle> <Style TargetType="TextBlock"> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </DataGridTextColumn.ElementStyle> </DataGridTextColumn> <DataGridTextColumn Header="High" Binding="{Binding High, StringFormat=F2}" Width="70" IsReadOnly="True"> <DataGridTextColumn.ElementStyle> <Style TargetType="TextBlock"> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </DataGridTextColumn.ElementStyle> </DataGridTextColumn> <DataGridTextColumn Header="Low" Binding="{Binding Low, StringFormat=F2}" Width="70" IsReadOnly="True"> <DataGridTextColumn.ElementStyle> <Style TargetType="TextBlock"> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </DataGridTextColumn.ElementStyle> </DataGridTextColumn> </DataGrid.Columns> </DataGrid>
1.ItemsSource
表示するリストを設定する
2.AutoGenerateColumns
ON:リストに入っているデータを自動的にカラムに設定して表示
OFF:自動的にカラムを設定しない
3.HorizontalContentAlignment
ヘッダーの文字列の中央揃え
<DataGrid.ColumnHeaderStyle> <Style TargetType="DataGridColumnHeader"> <Setter Property="HorizontalContentAlignment" Value="Center"/> </Style> </DataGrid.ColumnHeaderStyle>
4.DataGridTextColumn
カラムの設定
5.DataGridTextColumn.ElementStyle
Cellの文字列の中央揃え
<DataGridTextColumn.ElementStyle> <Style TargetType="TextBlock"> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </DataGridTextColumn.ElementStyle>
StockDataView.xaml.cs
メインXAMLのコードビハインド
namespace StockData.View { public partial class StockDataView : Window { private StockDataViewModel _stockDataViewModel; public StockDataView(StockDataViewModel stockDataViewModel) { _stockDataViewModel = stockDataViewModel; InitializeComponent(); Messenger.Default.Register<string>(this, "ErrorMsg", ErrorMsg); } public void ErrorMsg(string msg) { MessageBox.Show(msg, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } }
メッセンジャー
メッセンジャーを使う場合はNugetからPackageのインストールが必要になります。
MvvmLight:v5.4.1.1
MvvmLightLibs:v5.4.1.1
Model or ViewModelからViewへ通知を送る方法
Messenger.Default.Register<string>(this, "ErrorMsg", ErrorMsg);
public void ErrorMsg(string msg) { MessageBox.Show(msg, "Error", MessageBoxButton.OK, MessageBoxImage.Error); }
DateValidationRule
独自のValidationルールの設定
namespace StockData.Validation { public class DateValidationRule : ValidationRule { public DateValidationRule() { } public override ValidationResult Validate(object value, CultureInfo cultureInfo) { if (value == null) return new ValidationResult(false, "値がnullです。"); DateTime date; if (DateTime.TryParse(value.ToString(), out date)) { return ValidationResult.ValidResult; } else { return new ValidationResult(false, "yyyy/MM/ddのフォーマットで入力してください。"); } } } }
ViewModel
StockDataViewModel.cs
StockDataViewと対になるStockDataViewModel
namespace StockData.ViewModel { public class StockDataViewModel : BaseProperty { private Condition _condition; private ObservableCollection<Stock> _stockList = new ObservableCollection<Stock>(); public StockDataViewModel() { //CSV読み込み CsvData.LoadStockList(); //条件設定 DateTime end = CsvData.GetStockList().AsEnumerable().Select(x => x.Date).Max(); DateTime start = end.AddMonths(-1); _condition = new Condition(start, end); //初期表示用のFilter FilterData(); } public void FilterData() { CsvData.GetStockList().AsParallel().ForAll(x => Condition.InRange(x)); StockList = new ObservableCollection<Stock>(CsvData.GetStockList().Where(x => x.Visible).OrderByDescending(x => x.Date)); } #region getter/setter public Condition Condition { get { return _condition; } set { if (_condition != value) { _condition = value; OnPropertyChanged("Condition"); } } } public ObservableCollection<Stock> StockList { get { return _stockList; } set { if (_stockList != value) { _stockList = value; OnPropertyChanged("StockList"); } } } #endregion #region command public ICommand FilterCommand { get { return new BaseICommand(FilterCommandCallBack); } } private void FilterCommandCallBack() { if (!Condition.Check()) { Messenger.Default.Send("StartはEndより前の日付を入力してください。", "ErrorMsg"); } else { FilterData(); } } public ICommand OpenChartCommand { get { return new BaseICommand(OpenChartCommandCallBack); } } private void OpenChartCommandCallBack() { Messenger.Default.Send(string.Empty, "OpenChartView"); } #endregion } }
こちらは特段説明をする必要はないかと思います。
BaseICommand.cs
ICommandを継承したクラス
namespace StockData.ViewModel { public class BaseICommand : ICommand { public delegate void DelegateAction(); private DelegateAction _delegateAction; public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } private Func<bool> _canExecuteFunc; public BaseICommand(Action action) : this(action, null) { } public BaseICommand(Action action, Func<bool> canExe) { _delegateAction = new DelegateAction(action); _canExecuteFunc = canExe; } public bool CanExecute(object parameter) { if (_canExecuteFunc == null) { return true; } else { return _canExecuteFunc.Invoke(); } } public void Execute(object parameter) { _delegateAction(); } } }
Commandの使い方は下記のブログをご覧ください。
C# WPF-Tips-CommandをButtonのトリガーに - エンジニアの備忘録
完成
これで全コードを載せ、説明しました。
わからない箇所がありましたら、コメント頂けますと
説明を追加します。
次回予告
データを表形式で表示するだけではやっぱりつまらないですね。
次は取得したデータをチャートにして表示させようと思います。