職場の若手向けお勉強テーマとして作成しました。C#(WPF) で下記の機能を実現しなさい、という内容です。
カウンタ表示部と、内部変数、をデータバインディングします。
画面仕様は以下の通り。記載事項以外は自由に設計のこと。
フォント: Arial
項番 |
概要 |
コントロール |
タブ |
文字列、文字種別、他 |
他 |
① |
|
- |
|
“Counter” |
Windowサイズ:800×600以内 |
② |
値表示部 |
TextBox |
(*1) |
Number,
値範囲:0~10 |
フォントサイズ:36pt
センター表示
ReadOnly |
③ |
+1 |
Button |
1 |
“+” |
フォントサイズ:12pt |
④ |
-1 |
Button |
2 |
“-“ |
フォントサイズ:12pt |
⑤ |
0クリア |
Button |
3 |
“Clear” |
フォントサイズ:12pt |
⑥ |
プログラム
終了ボタン |
Button |
0 |
“Quit” |
フォントサイズ:12pt |
(*1) TabStop を False に設定
動作環境: Windows XP または Windows7
開発環境: Visual Studio 2008 以降
開発言語: C#(WPF)
実際に私が作成したプログラムの画面は下図の通り。
TextBoxの部分だけ 色づけ
を遊んでみました。パネルが曲面を描いているように見えれば幸いです。
実作業は Expression Blend
で行いました。Blendが無いとWPFの実装は面白くないですね。
WPF では、XAMLとC#コード間のデータバインディングを行うために "DataContext" というメンバ変数を介して行うようです。
[プログラムソース (Window1.xaml)]
<Window
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" mc:Ignorable="d"
xmlns:CounterDll="clr-namespace:CounterDll;assembly=CounterDll" x:Class="CounterWPF.Window1"
Title="Counter on the WPF" Height="237.333" Width="347.467"
ResizeMode="NoResize" Background="White" Closing="Window_Closing">
<Grid>
<Button x:Name="Button_Quit" HorizontalAlignment="Right" Margin="8,0,8,8" Width="100"
Content="Quit" VerticalAlignment="Bottom" Height="50" FontFamily="Arial" FontSize="16"
Click="Button_Click_Quit" ToolTip="プログラムを終了します。" />
<TextBox x:Name="TextBox_CounterView" Margin="8,8,8,0" VerticalAlignment="Top" Height="59"
TextWrapping="Wrap" FontFamily="Arial" FontSize="48" TextAlignment="Center"
Text="{Binding Path=counter.CountValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsReadOnly="True" IsTabStop="False" ToolTip="値を表示します。">
<TextBox.Background>
<LinearGradientBrush EndPoint="0.496,0.022" StartPoint="0.498,0.936">
<GradientStop Color="#FF8392AD" Offset="0.004"/>
<GradientStop Color="#FFE2E5EA" Offset="1"/>
</LinearGradientBrush>
</TextBox.Background>
<TextBox.Effect>
<DropShadowEffect Direction="135" ShadowDepth="2"/>
</TextBox.Effect>
</TextBox>
<Button x:Name="Button_Increment" HorizontalAlignment="Left" Margin="8,80,0,0" Width="100"
Content="+" FontFamily="Arial" FontSize="16" Click="Button_Click_Increment"
ToolTip="値を1つ加算します。" Height="50" VerticalAlignment="Top" />
<Button x:Name="Button_Decrement" Margin="0,80,0,0" Content="-" FontFamily="Arial" FontSize="16"
Click="Button_Click_Decrement" ToolTip="値を1つ減算します。" Width="100"
HorizontalAlignment="Center" VerticalAlignment="Top" Height="50" />
<Button x:Name="Button_Clear" Margin="0,80,8,0" Content="Clear" HorizontalAlignment="Right"
Width="100" FontFamily="Arial" FontSize="16" Click="Button_Click_Clear"
ToolTip="値を0にクリアします。" Height="50" VerticalAlignment="Top" />
<Rectangle Fill="White" Margin="8,14.6,8,0" VerticalAlignment="Top" Height="13.6" Opacity="0.85">
<Rectangle.Effect>
<BlurEffect Radius="10"/>
</Rectangle.Effect>
</Rectangle>
</Grid>
</Window>
[プログラムソース (Window1.xaml.cs)]
using System; using System.Collections.Generic; using System.Linq; using System.Text; 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; using CounterDll; namespace CounterWPF { /// <summary> /// Window1.xaml の相互作用ロジック /// </summary> public partial class Window1 : Window { private Counter _counter = new Counter(); public Window1() { InitializeComponent(); // DataContext = _counter; DataContext = new { counter = _counter, }; Button_Click_Clear(this, new RoutedEventArgs()); } private void Button_Click_Quit(object sender, System.Windows.RoutedEventArgs e) { // TODO: ここにイベント ハンドラーのコードを追加します。 Close(); } private void Button_Click_Increment(object sender, System.Windows.RoutedEventArgs e) { _counter.Increment(); if (_counter.CountValue == _counter.CountMax) { Button_Increment.IsEnabled = false; } if (_counter.CountValue != _counter.CountMin) { Button_Decrement.IsEnabled = true; } } private void Button_Click_Decrement(object sender, System.Windows.RoutedEventArgs e) { _counter.Decrement(); if (_counter.CountValue != _counter.CountMax) { Button_Increment.IsEnabled = true; } if (_counter.CountValue == _counter.CountMin) { Button_Decrement.IsEnabled = false; } } private void Button_Click_Clear(object sender, System.Windows.RoutedEventArgs e) { _counter.Clear(); Button_Increment.IsEnabled = true; Button_Decrement.IsEnabled = false; } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { if (MessageBox.Show("プログラムを終了してよろしいですか?", "終了確認", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) { // プログラムを終了する } else { // プログラムを終了しない e.Cancel = true; } } } }
[プログラムソース (Counter.cs)]
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; // INotifyPropertyChanged [assembly: CLSCompliant(true)] // CA1014 namespace CounterDll { public class Counter : INotifyPropertyChanged { private int _countValue ; public int CountValue { get { return _countValue; } set { _countValue = value; NotifyPropertyChanged("CountValue"); } } private readonly int _CountMax = 10; public int CountMax { get { return _CountMax; } } private readonly int _CountMin = 0; public int CountMin { get { return _CountMin; } } public void Increment() { if (_countValue < _CountMax) { CountValue++; } } public void Decrement() { if (_countValue > _CountMin) { CountValue--; } } public void Clear() { CountValue = 0; } #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; void NotifyPropertyChanged(string info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } #endregion // INotifyPropertyChanged } }
BUTTON のコマンドをモデル部に結び付けます。
ここでは(その1)からの改造ではなくて新規作成のイメージで進めます。理由は、この手順で進めた場合にはコントロールへのデータ・バインディングがVSからGUIで実施しやすくなることを示すためです。
以下にプログラムを記載します。
class Counter をDLLとして作成します。
DLLでなくても動作するはずです。
["Counter.cs"]
using System; [assembly: CLSCompliant(true)] // CA1014 namespace CounterDll { public class Counter : ViewModel { #region ICommands // +ボタンで呼ばれるコマンド public DelegateCommand IncrementCommand { get; private set; } // -ボタンで呼ばれるコマンド public DelegateCommand DecrementCommand { get; private set; } // Clearボタンで呼ばれるコマンド public DelegateCommand ClearCommand { get; private set; } #endregion ICommand #region countValue private int _countValue ; public int CountValue { get { return _countValue; } set { _countValue = value; NotifyPropertyChanged("CountValue"); IncrementCommand.NotifyCanExcuteChanged(); DecrementCommand.NotifyCanExcuteChanged(); } } private readonly int _CountMax = 10; public int CountMax { get { return _CountMax; } } private readonly int _CountMin = 0; public int CountMin { get { return _CountMin; } } #endregion countValue /// <summary> /// コンストラクタ /// </summary> public Counter() { // + コマンドを実装 IncrementCommand = new DelegateCommand( () => { Increment(); }, () => { bool bCanExecute = true; if ( CountValue >= CountMax) { bCanExecute = false; } return bCanExecute; }); // - コマンドを実装 DecrementCommand = new DelegateCommand( () => { Decrement(); }, () => { bool bCanExecute = true; if (CountValue <= CountMin) { bCanExecute = false; } return bCanExecute; }); // Clear コマンドを実装 ClearCommand = new DelegateCommand( () => { Clear(); }); } private void Increment() { if (_countValue < _CountMax) { CountValue++; } } private void Decrement() { if (_countValue > _CountMin) { CountValue--; } } private void Clear() { CountValue = 0; } } }
[ViewModel.cs]
using System.ComponentModel; // INotifyPropertyChanged using System.Runtime.CompilerServices; // CallerMemberName namespace CounterDll { public class ViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = null) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } }
[DelegateCommand.cs]
using System; using System.Windows.Input; // ICommand namespace CounterDll { public class DelegateCommand : ICommand { private readonly Action _execute; private readonly Func<bool> _canExecute; public DelegateCommand(Action execute) : this(execute, () => true) { } public DelegateCommand(Action execute, Func<bool> canExecute) { this._execute = execute; this._canExecute = canExecute; } public void Execute(object parameter) { this._execute(); } public bool CanExecute(object parameter) { return this._canExecute(); } public event EventHandler CanExecuteChanged; public void NotifyCanExcuteChanged() { if (CanExecuteChanged != null) { CanExecuteChanged(this, EventArgs.Empty); } } } }
では本体アプリ側を修正します。
先ほど作成した Counter.dll を参照設定しておくのを忘れずに。
MainWindow.xaml.cs です。こちら、ほとんど空っぽになりました。
[MainWindow.xaml.cs]
using System.Windows; namespace CounterWPF { public partial class Window1 : Window { public Window1() { InitializeComponent(); } private void Button_Click_Quit(object sender, System.Windows.RoutedEventArgs e) { Close(); } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { if (MessageBox.Show("プログラムを終了してよろしいですか?", "終了確認", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) { // プログラムを終了する } else { // プログラムを終了しない e.Cancel = true; } } } }
Window の DataContext へ Counter を新規登録します。
Window を選択後、DataContext
の右側にある「新規作成」ボタンをクリックしてください。
「オブジェクトの選択」画面を表示するので、ここから CounterDll から class Counter を選択後、「OK」ボタンをクリックします。
これで Window の DataContext へ Counter を登録することができました。
この時点の MainWindow.xaml.cs は以下の通りです。
<Window
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" mc:Ignorable="d"
xmlns:CounterDll="clr-namespace:CounterDll;assembly=CounterDll" x:Class="CounterWPF.Window1"
Title="Counter on the WPF" Height="237.333" Width="347.467" ResizeMode="NoResize"
Background="White" Closing="Window_Closing">
<Window.DataContext>
<CounterDll:Counter/>
</Window.DataContext>
<Grid>
<Button x:Name="Button_Quit" HorizontalAlignment="Right" Margin="8,0,8,8" Width="100"
Content="Quit" VerticalAlignment="Bottom" Height="50" FontFamily="Arial" FontSize="16"
Click="Button_Click_Quit" ToolTip="プログラムを終了します。" />
<TextBox x:Name="TextBox_CounterView" Margin="8,8,8,0" VerticalAlignment="Top" Height="59"
TextWrapping="Wrap" FontFamily="Arial" FontSize="48" TextAlignment="Center" IsReadOnly="True"
IsTabStop="False" ToolTip="値を表示します。">
<TextBox.Background>
<LinearGradientBrush EndPoint="0.496,0.022" StartPoint="0.498,0.936">
<GradientStop Color="#FF8392AD" Offset="0.004"/>
<GradientStop Color="#FFE2E5EA" Offset="1"/>
</LinearGradientBrush>
</TextBox.Background>
<TextBox.Effect>
<DropShadowEffect Direction="135" ShadowDepth="2"/>
</TextBox.Effect>
</TextBox>
<Button x:Name="Button_Increment" HorizontalAlignment="Left" Margin="8,80,0,0" Width="100"
Content="+" FontFamily="Arial" FontSize="16" ToolTip="値を1つ加算します。" Height="50"
VerticalAlignment="Top" />
<Button x:Name="Button_Decrement" Margin="0,80,0,0" Width="100" Height="50"
Content="-" FontFamily="Arial" FontSize="16"
ToolTip="値を1つ減算します。" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<Button x:Name="Button_Clear" Margin="0,80,8,0" Content="Clear" HorizontalAlignment="Right"
Width="100" FontFamily="Arial" FontSize="16" ToolTip="値を0にクリアします。" Height="50"
VerticalAlignment="Top" />
<Rectangle Fill="White" Margin="8,14.6,8,0" VerticalAlignment="Top" Height="13.6" Opacity="0.85">
<Rectangle.Effect>
<BlurEffect Radius="10"/>
</Rectangle.Effect>
</Rectangle>
</Grid>
</Window>
TextBox の Text を DataContext の Counter.CountValue へデータ・バインディングします。
TextBox を選択後、Text の右側にある□をクリック、「データ・バインディング の作成...」を選びます。
「TextBox_CounterView.Text のデータ バインディングを作成」の画面を表示するので、パスから「CounterValue」を選択して「OK」ボタンを押します。
下図の画面になったら成功です。TextBox の Text が CountValue へ結びついて "0" を表示できるようになりました。
「+」ボタンのコマンドを Counter.IncrementCommand へバインディングします。
「+」ボタンを選択後、Command の右側にある□をクリック、
パスから「IncrementCommand」を選択し、「OK」をクリックします。
3.2.4. と同様の手順で「ー」、「Clear」ボタンをバインディングします。
この時点の MainWindow.xaml.cs は以下の通りです。
<Window 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" mc:Ignorable="d" xmlns:CounterDll="clr-namespace:CounterDll;assembly=CounterDll" x:Class="CounterWPF.Window1" Title="Counter on the WPF" Height="237.333" Width="347.467" ResizeMode="NoResize" Background="White" Closing="Window_Closing"> <Window.DataContext> <CounterDll:Counter/> </Window.DataContext> <Grid> <Button x:Name="Button_Quit" HorizontalAlignment="Right" Margin="8,0,8,8" Width="100" Content="Quit" VerticalAlignment="Bottom" Height="50" FontFamily="Arial" FontSize="16" Click="Button_Click_Quit" ToolTip="プログラムを終了します。" /> <TextBox x:Name="TextBox_CounterView" Margin="8,8,8,0" VerticalAlignment="Top" Height="59" TextWrapping="Wrap" FontFamily="Arial" FontSize="48" TextAlignment="Center" IsReadOnly="True" IsTabStop="False" ToolTip="値を表示します。" Text="{Binding CountValue}"> <TextBox.Background> <LinearGradientBrush EndPoint="0.496,0.022" StartPoint="0.498,0.936"> <GradientStop Color="#FF8392AD" Offset="0.004"/> <GradientStop Color="#FFE2E5EA" Offset="1"/> </LinearGradientBrush> </TextBox.Background> <TextBox.Effect> <DropShadowEffect Direction="135" ShadowDepth="2"/> </TextBox.Effect> </TextBox> <Button x:Name="Button_Increment" HorizontalAlignment="Left" Margin="8,80,0,0" Width="100" Content="+" FontFamily="Arial" FontSize="16" ToolTip="値を1つ加算します。" Height="50" VerticalAlignment="Top" Command="{Binding IncrementCommand, Mode=OneWay}" /> <Button x:Name="Button_Decrement"HorizontalAlignment="Center"
Margin="0,80,0,0" Content="-"Width="100"
FontFamily="Arial" FontSize="16" ToolTip="値を1つ減算します。" VerticalAlignment="Top"Height="50"
Command="{Binding DecrementCommand, Mode=OneWay}" /> <Button x:Name="Button_Clear" Margin="0,80,8,0" Content="Clear" HorizontalAlignment="Right" Width="100" FontFamily="Arial" FontSize="16" ToolTip="値を0にクリアします。" Height="50" VerticalAlignment="Top" Command="{Binding ClearCommand, Mode=OneWay}" /> <Rectangle Fill="White" Margin="8,14.6,8,0" VerticalAlignment="Top" Height="13.6" Opacity="0.85"> <Rectangle.Effect> <BlurEffect Radius="10"/> </Rectangle.Effect> </Rectangle> </Grid> </Window>
以上で完成です。
本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。
2022-08-10 | - | ページデザイン更新 |
2016-05-03 | - | 更新 |
2012-05-16 | - | 新規作成 |