職場の若手向けお勉強テーマとして作成しました。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 | - | 新規作成 |