Very new to WPF coding using MVVM. Tried making a simple calculator in WPF using MVVM. But unable to trigger the Icommand in the below code.If possible help me in this. Grateful if anybody can help me out.
View Code:
<Window x:Class="MVVMCalculator.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:MVVMCalculator"
mc:Ignorable="d"
Title="Calculator" Height="350" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="85"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Text="{Binding Display, Mode=OneWay}" IsReadOnly="True" TextWrapping="Wrap"
Grid.Row="0" Background="#E2E2E2" Margin="0,10,0,0" VerticalAlignment="Top"
Height="75" Width="250" HorizontalAlignment="Center" FontSize="22" FontWeight="Bold"
TextAlignment="Right">
<TextBox.Effect>
<DropShadowEffect/>
</TextBox.Effect>
</TextBox>
<ItemsControl Grid.Row="1" ItemsSource="{Binding Buttns}" Margin="15,15,15,10">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="5" Rows="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Txt, Mode=TwoWay}" Command="{Binding Enter_number}"
FontSize="18" FontWeight="Bold" Height="50" Width="50" Background="#eef2f3"
BorderBrush="Black" BorderThickness="1.0" Name="number">
<Button.Effect>
<DropShadowEffect/>
</Button.Effect>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
ViewModel Code:
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.Input;
namespace MVVMCalculator
{
class ViewModel : INotifyPropertyChanged
{
Buttons btn = new Buttons();
private decimal operand1;
private decimal operand2;
private string operation;
private decimal result;
private string display;
private bool newDisplayRequired = false;
ObservableCollection<Buttons> buttns;
public ObservableCollection<Buttons> Buttns
{
get { return buttns; }
set { buttns = value; }
}
public decimal Result
{
get { return result; }
}
public decimal Operand1
{
get { return operand1; }
set { operand1 = value; }
}
public decimal Operand2
{
get { return operand2; }
set { operand2 = value; }
}
public string Operation
{
get { return operation; }
set { operation = value; }
}
public string Display
{
get { return display; }
set { display = value;
OnPropertyChanged("Display");
}
}
public ViewModel()
{
buttns = new ObservableCollection<Buttons>
{
new Buttons("1"), new Buttons("2"), new Buttons("3"),
new Buttons("C"), new Buttons("Back"), new Buttons("4"),
new Buttons("5"), new Buttons("6"), new Buttons("CE"),
new Buttons("%"), new Buttons("7"), new Buttons("8"),
new Buttons("9"), new Buttons("/"), new Buttons("*"),
new Buttons("0"), new Buttons("."), new Buttons("+"),
new Buttons("-"), new Buttons("=")
};
display = "0";
operand1 = 0;
operand2 = 0;
operation = "";
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private ICommand enter_number;
public ICommand Enter_number
{
get
{
if(enter_number==null)
{
enter_number = new DelegateCommand<string>(MyAction, _canExecute);
}
return enter_number;
}
}
private static bool _canExecute(string button)
{
return true;
}
public void MyAction(string btn)
{
switch(btn)
{
case "C":
display = "0";
operand1 = 0;
operand2 = 0;
//operation = "";
break;
case ".":
if (!display.Contains("."))
{
Display = display + ".";
}
break;
case "Back":
if (display.Length > 1)
Display = display.Substring(0, display.Length - 1);
else Display = "0";
break;
default:
if (display == "0" || newDisplayRequired)
Display = btn;
else
Display = display + btn;
break;
}
}
}
}
Buttons Class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MVVMCalculator
{
class Buttons:INotifyPropertyChanged
{
private string txt;
public string Txt
{
get { return txt; }
set { txt = value; }
}
public Buttons(string a)
{
txt = a;
}
public Buttons()
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Xaml.cs:
using System;
using System.Collections.Generic;
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 MVVMCalculator
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
}
Since the Enter_number
property is defined in the ViewModel
class you need to use a {RelativeSource}
to be able to bind to it:
<Button Content="{Binding Txt, Mode=TwoWay}"
Command="{Binding DataContext.Enter_number, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
FontSize="18" FontWeight="Bold" Height="50" Width="50" Background="#eef2f3"
BorderBrush="Black" BorderThickness="1.0" Name="number">
<Button.Effect>
<DropShadowEffect/>
</Button.Effect>
</Button>
The default DataContext
of the Button
is the current Buttons
object in the ItemsSource
collection of the ItemsControl
and that's why your binding fails.