I am working on WPF C#, MVVM Model. I have issue with Save_Button in View. All the things like getter, setter, RelayCommand initialization are working, just nothing happens when I click on 'Save' Buton. So it seems like Binding from View to ViewModel is not working. I am providing here only necessary files of View, ViewModel and Command part. Kindly help.
VehicalForm.xaml
<Window x:Class="Seris.VehicalForm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<WrapPanel Orientation="Vertical" Margin="10 " >
<Label Content="Vehical No" HorizontalAlignment="Left"/>
<TextBox Name="VehicalNo_Text" Height="23" TextWrapping="Wrap" Text="TextBox" HorizontalAlignment="Left"/>
<Label Content="Model" HorizontalAlignment="Left"/>
<TextBox Name="Model_Text" Height="23" TextWrapping="Wrap" Text="TextBox" HorizontalAlignment="Left" />
<Label Content="Manufacturing Date" HorizontalAlignment="Left"/>
<DatePicker/>
<Label Content="IU No" HorizontalAlignment="Left"/>
<TextBox Height="23" Name="IUNO_Text" TextWrapping="Wrap" Text="TextBox" HorizontalAlignment="Left"/>
<Label Content="Personnel" HorizontalAlignment="Left"/>
<ComboBox Name="Personnel_Combo" HorizontalAlignment="Left" Width="116"/>
<Separator Height="20" RenderTransformOrigin="0.5,0.5" Width="16"/>
<Button Name="Save_Button" Command="{Binding SaveToList}" Content="Save" Width="66"/>
<ListView Height="294" Width="371" >
<ListView.View>
<GridView>
<GridViewColumn Header="lkj"/>
<GridViewColumn Header="lkj"/>
<GridViewColumn Header="lkj"/>
</GridView>
</ListView.View>
</ListView>
</WrapPanel>
VehicalForm.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;
namespace Seris
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class VehicalForm : Window
{
public VehicalForm()
{
InitializeComponent();
}
}
}
VehicalMainViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Seris.Models;
using System.Collections.ObjectModel;
using System.Windows.Input;
using Seris.Commands;
using Seris.ViewModels;
namespace Seris.ViewModels
{
public class VehicalMainViewModel : ObservableObject
{
ObservableCollection<VehicalModel> listItems = new ObservableCollection<VehicalModel>();
#region Getter-Setter
private string _VehicalNo;
public string VehicalNo
{
get { return _VehicalNo; }
set
{
if (value != _VehicalNo)
{
_VehicalNo = value.Trim();
OnPropertyChanged("ProductName");
}
}
}
private string _Model;
public string Model
{
get { return _Model; }
set
{
if (value != _Model)
{
_Model = value.Trim();
OnPropertyChanged("ProductName");
}
}
}
private DateTime _ManufacturingDate;
public DateTime ManufacturingDate
{
get { return _ManufacturingDate; }
set
{
if (value != _ManufacturingDate)
{
_ManufacturingDate = value;
OnPropertyChanged("ProductName");
}
}
}
private string _IUNo;
public string IUNo
{
get { return _IUNo; }
set
{
if (value != _IUNo)
{
_IUNo = value.Trim();
OnPropertyChanged("ProductName");
}
}
}
private string _PersonnelName;
public string PersonnelName
{
get { return _PersonnelName; }
set
{
if (value != _PersonnelName)
{
_PersonnelName = value.Trim();
OnPropertyChanged("ProductName");
}
}
}
#endregion
private ICommand _saveButton_Command;
public ICommand SaveButton_Command
{
get { return _saveButton_Command; }
set { _saveButton_Command = value; }
}
public void SaveToList(object o1)
{
listItems.Add(new VehicalModel(VehicalNo,Model,ManufacturingDate,IUNo,PersonnelName));
}
public void RemoveFromList()
{
}
public VehicalMainViewModel()
{
VehicalModel vm=new VehicalModel();
SaveButton_Command = new RelayCommand(new Action<object>(SaveToList));
}
}
}
RelayCommand.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
namespace Seris.Commands
{
public class RelayCommand : ICommand
{
private Action<object> _action;
public RelayCommand(Action<object> action)
{
_action = action;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action(parameter);
}
}
}
try in XAML
<Button Name="Save_Button" Command="{Binding SaveButton_Command}" … />
You tried to Bind to the Method instead of the Command.
<Button Name="Save_Button" Command="{Binding SaveButton_Command}" />
Have you set the DataContext in on your VehicalForm-Window?
public VehicalForm()
{
InitializeComponent();
this.DataContext = new VehicalMainViewModel();
}
Related
I get the error message which is written in the title.
My XAML looks like this:
<Window x:Class="LINQ3_Test_aus_LINQ2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LINQ3_Test_aus_LINQ2"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<TextBox x:Name="txt_LiefNr" HorizontalAlignment="Left" Height="26" Margin="55,50,0,0" TextWrapping="Wrap" Text="{Binding LiefNr}" VerticalAlignment="Top" Width="125"/>
<TextBox x:Name="txt_LiefName" HorizontalAlignment="Left" Height="26" Margin="55,93,0,0" TextWrapping="Wrap" Text="{Binding LiefName}" VerticalAlignment="Top" Width="125"/>
<Button x:Name="btn_LiefNr" Content="Button" HorizontalAlignment="Left" Height="26" Margin="215,50,0,0" VerticalAlignment="Top" Width="116" Command="{Binding LookupCommand}"/>
</Grid>
and my MainViewModel.cs like this:
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace LINQ3_Test_aus_LINQ2
{
public class MainViewModel : INotifyPropertyChanged
{
private string _liefNr;
private string _liefName;
public ICommand LookupCommand { get; set; }
public string LiefNr
{
get { return _liefNr; }
set
{
_liefNr = value;
OnPropertyChanged();
}
}
public string LiefName
{
get { return _liefName; }
set
{
_liefName = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
LookupCommand = new RelayCommand(Lookup);
}
private void Lookup()
{
Excel.OpenFile();
LiefName = Excel.Fill(LiefNr);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I cant find a solution to this error thats why i am asking you. I have checked multiple times if something is wrong the the names, but there is not its all written correctly.
I have two WPF windows and i want to set the context to a ViewModel but if I write:
this.DataContext = new myViewModel()
in my second windows cs it doesn't work here is my code. i have tried to place a binding in the XAML and to connect the context but when i try to debug it all i get the error code this breakpoint will not get run.
BrowseDialog.xaml
<Window x:Class="TextalkApi.BrowseDialog"
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:TextalkApi"
mc:Ignorable="d"
Title="BrowseDialog" Height="248.361" Width="427.459">
<Grid>
<Button Content="Browse" HorizontalAlignment="Left" Margin="267,11,0,0" VerticalAlignment="Top" Width="75"/>
<TextBox x:Name="FileDialog" HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding webUrl}" VerticalAlignment="Top" Width="244"/>
<Button Content="Save" HorizontalAlignment="Left" Margin="267,166,0,0" VerticalAlignment="Top" Width="75" Command="{Binding SaveCommand}" />
<TextBox HorizontalAlignment="Left" Height="23" Margin="10,38,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Label Content="{Binding errorMessage}" HorizontalAlignment="Left" Margin="10,167,0,0" VerticalAlignment="Top" RenderTransformOrigin="-5.611,10.822" Width="207" Height="19"/>
</Grid>
</Window>
BrowseViewModel
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;
using System.Threading;
using System.Threading.Tasks;
using System.Configuration;
using System.Collections.Specialized;
using System.IO;
namespace Data
{
public class BrowseViewModel : BaseViewModel
{
#region public variables
public string webUrl { get; set; }
public string errorMessage { get; set; }
#endregion
#region Public Commands
public ICommand SaveCommand { get; set; }
#endregion
#region Constructor
public BrowseViewModel()
{
this.SaveCommand = new RelayCommand(SaveFilePath);
}
#endregion
#region Private methods
private void SaveFilePath()
{
if (File.Exists(webUrl))
{
ConfigurationManager.AppSettings.Add("WebUrl", webUrl);
}
else
{
errorMessage = "Filen existerar ej";
}
}
#endregion
}
}
BrowseDialog.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.Shapes;
using System.IO;
using Data;
namespace TextalkApi
{
/// <summary>
/// Interaction logic for BrowseDialog.xaml
/// </summary>
public partial class BrowseDialog : Window
{
public BrowseDialog()
{
InitializeComponent();
this.DataContext = new BrowseViewModel();
}
}
}
BaseViewModel
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using PropertyChanged;
namespace Data
{
[AddINotifyPropertyChangedInterface]
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
}
Your XAML looks correct and I highly suspect it is the ViewModel causing it not to work. Something like this should work.
Please note that this will depend a lot on how the BaseViewModel has been implemented. If possible can you share this so I can update my answer to be correct? The below is how you should implement the properties in your ViewModel which is the DataContext of the View.
#region Properties
private string _webUrl;
public string WebUrl
{
get => _webUrl;
set
{
//This will change based on how you have implemented your BaseViewModel!
//The method name might be different, or have different parameters!
this.SetProperty(ref _webUrl, value, nameof(WebUrl));
//Call the save file path validation method...
SaveFilePath();
}
}
private string _errorMessage;
public string ErrorMessage
{
get => _errorMessage;
private set
{
//This will change based on how you have implemented your BaseViewModel!
//This method should call NotifyPropertyChange to notify the UI to update...
this.SetProperty(ref _errorMessage, value, nameof(ErrorMessage));
}
}
#endregion
In your ViewModelBase you can add a generic SetProperty method that can then handle raising the property changed event for you. Something like this:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void SetProperty<T>(ref T storage, T value, string propertyName)
{
storage = value;
RaisePropertyChangedEvent(propertyName);
}
protected void RaisePropertyChangedEvent(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and because of the changes in the ViewModel you will also need to update the bindings in your XAML.
<Grid>
<Button Content="Browse" HorizontalAlignment="Left" Margin="267,11,0,0" VerticalAlignment="Top" Width="75"/>
<TextBox x:Name="FileDialog" HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding WebUrl}" VerticalAlignment="Top" Width="244"/>
<Button Content="Save" HorizontalAlignment="Left" Margin="267,166,0,0" VerticalAlignment="Top" Width="75" Command="{Binding SaveCommand}" />
<TextBox HorizontalAlignment="Left" Height="23" Margin="10,38,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Label Content="{Binding ErrorMessage}" HorizontalAlignment="Left" Margin="10,155,0,0" VerticalAlignment="Top" RenderTransformOrigin="-5.611,10.822" Width="207" Height="46"/>
</Grid>
I have a small program, because I'm new, my program is to bring data from sql sever to textbox via combobox option and use the value shown in that textbox to calculate the +
I have made it to the step of putting up the data, now thanks to you to help me with the value calculation in the textbox, thank you for your help.
xaml code :
<Window x:Class="comboboxapp1.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:comboboxapp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:SimpleMath x:Key="MyFriends"/>
</Window.Resources>
<Grid>
<Label Content="code" HorizontalAlignment="Left" Margin="38,52,0,0"
VerticalAlignment="Top" Width="46" Height="23"/>
<Label Content="pieces" HorizontalAlignment="Left" Margin="38,126,0,0"
VerticalAlignment="Top" Width="46" Height="23"/>
<Label Content="layers" HorizontalAlignment="Left" Margin="38,196,0,0"
VerticalAlignment="Top" Width="46" Height="30"/>
<Label Content="production pieces" HorizontalAlignment="Left"
Margin="0,278,0,0" VerticalAlignment="Top" Width="108" Height="25"/>
<TextBox x:Name="txtcode"
Text="{Binding Txtcode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Height="23" Margin="124,52,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="141"/>
<TextBox x:Name="txtpieces"
Text="{Binding Txtpieces, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Height="23"
Margin="124,133,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="141"/>
<TextBox x:Name="txtlayers"
Text="{Binding Txtlayers,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Height="23"
Margin="124,203,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="141"/>
<TextBox x:Name="txtproductionpieces"
Text="{Binding Txtproductionpieces,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Height="23" Margin="124,280,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="141"/>
<ComboBox x:Name="comboBox1" ItemsSource="{Binding Source={StaticResource MyFriends}}"
HorizontalAlignment="Left" Margin="418,52,0,0" VerticalAlignment="Top"
Width="319" Height="36" SelectionChanged="ComboBox1_SelectionChanged"/>
<TextBox x:Name="txtseccond"
Text="{Binding Txtseccond,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Height="23"
Margin="124,345,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="141"/>
<Label Content="seccond" HorizontalAlignment="Left" Margin="38,345,0,0"
VerticalAlignment="Top" Width="46" Height="23"/>
<TextBlock Text="{Binding A, Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Margin="418,133,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Height="23" Width="248"/>
<TextBox Text="{Binding No1,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Height="21" Margin="426,210,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="303"/>
</Grid>
</Window>
C# code:
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;
using System.Data.SqlClient;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace comboboxapp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public SimpleMath Formular { get; set; }
public object SelectedValue { get; private set; }
public MainWindow()
{
Formular = new SimpleMath()
{
Txtcode = 0,
Txtpieces = 0,
Txtlayers = 0,
Txtproductionpieces = 0,
Txtseccond = 0,
};
InitializeComponent();
DataContext = Formular;
Fillcombobox();
}
private void MainWindow_Load(object sender, EventArgs e)
{
}
public void Fillcombobox()
{
SqlConnection con = new SqlConnection("Data Source=LEAN-22\\SQLEXPRESS;Initial
Catalog=LUAT;Integrated Security=True");
string sql = " select * from comboboxnew ";
SqlCommand cmd = new SqlCommand(sql, con);
SqlDataReader myreader;
try
{
con.Open();
myreader = cmd.ExecuteReader();
while (myreader.Read())
{
string sname = myreader.GetInt32(0).ToString();
comboBox1.Items.Add(sname);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public class SimpleMath : INotifyPropertyChanged
{
private int no1;
public int No1
{
get { return no1; }
set
{
no1 = value;
OnPropertyChanged("No1");
OnPropertyChanged("A");
}
}
private int txtcode;
public int Txtcode
{
get { return txtcode; }
set
{
txtcode = value;
OnPropertyChanged("Txtcode");
OnPropertyChanged("A");
}
}
private int txtpieces;
public int Txtpieces
{
get { return txtpieces; }
set
{
txtpieces = value;
OnPropertyChanged("Txtcode");
OnPropertyChanged("A");
}
}
private int txtlayers;
public int Txtlayers
{
get { return txtlayers; }
set
{
txtlayers = value;
OnPropertyChanged("Txtlayers");
OnPropertyChanged("A");
}
}
private int txtproductionpieces;
public int Txtproductionpieces
{
get { return txtproductionpieces; }
set
{
txtproductionpieces = value;
OnPropertyChanged("Txtproductionpieces");
OnPropertyChanged("A");
}
}
private int txtseccond;
public int Txtseccond
{
get { return txtseccond; }
set
{
txtseccond = value;
OnPropertyChanged("Txtseccond");
OnPropertyChanged("A");
}
}
public double A => No1;
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName()] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
private void ComboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SqlConnection con = new SqlConnection("Data Source=LEAN-22\\SQLEXPRESS;Initial
Catalog=LUAT;Integrated Security=True");
// string sql = " select * from comboboxnew where code = '" + comboBox1.Text+ "';";
string sql = " select * from comboboxnew where code = '" + comboBox1.SelectedItem +
"';";
//Console.WriteLine(comboBox1.Text);
//MessageBox.Show(comboBox1.Text);
SqlCommand cmd = new SqlCommand(sql, con);
SqlDataReader myreader;
try
{
con.Open();
myreader = cmd.ExecuteReader();
while (myreader.Read())
{
string code = myreader.GetInt32(0).ToString();
string pieces = myreader.GetInt32(1).ToString();
string layers = myreader.GetInt32(2).ToString();
string productionpieces = myreader.GetInt32(3).ToString();
string seccond = myreader.GetInt32(4).ToString();
txtcode.Text = code;
//txtcode.Text =SelectedValue;
txtpieces.Text = pieces;
//txtpieces.Text = "New value";
txtlayers.Text = layers;
//txtlayers.Text = "New value";
txtproductionpieces.Text = productionpieces;
//txtproductionpieces.Text = "New value";
txtseccond.Text = seccond;
//txtseccond.Text = "New value";
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
From what I understand, you simply want to sum those five values into the TextBox, right?
If so, you can do that multiple ways...
One:
Have a property in the SimpleMath class that is the sum of the other values, and bind that to the TextBox. You would have to update the sum property whenever any of those five other ones updates.
Two:
You could use the IMultiValueConverter to sum the values without having any more properties in the SimpleMath class. It's done like this:
You create a class that implements the IMultiValueConverter inteface
public class SumConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.Cast<int>()?.Sum()?.ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
And use it in XAML like this
<TextBox>
<TextBox.Resources>
<local:SumConverter x:Key="SumConverter"/>
</TextBox.Resources>
<TextBox.Text>
<MultiBinding Converter="{StaticResource SumConverter}">
<Binding Path="Text" ElementName="OtherTextBox1" />
<Binding Path="Text" ElementName="OtherTextBox2" />
<Binding Path="Text" ElementName="OtherTextBox3" />
<Binding Path="Text" ElementName="OtherTextBox4" />
<Binding Path="Text" ElementName="OtherTextBox5" />
</MultiBinding>
</TextBox.Text>
</Control>
Hope this helps
EDIT
I've made a test project to show you the full code for both of my suggested solutions, here it is:
Solution one
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace StackOverflowTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new SimpleMath();
}
}
public class SimpleMath : INotifyPropertyChanged
{
private int _numberOne;
public int NumberOne
{
get => _numberOne;
set
{
_numberOne = value;
OnPropertyChanged();
CalculateSum();
}
}
private int _numberTwo;
public int NumberTwo
{
get => _numberTwo;
set
{
_numberTwo = value;
OnPropertyChanged();
CalculateSum();
}
}
private int _numberThree;
public int NumberThree
{
get => _numberThree;
set
{
_numberThree = value;
OnPropertyChanged();
CalculateSum();
}
}
private int _numberSum;
public int NumberSum
{
get => _numberSum;
set
{
_numberSum = value;
OnPropertyChanged();
}
}
private void CalculateSum()
{
NumberSum = NumberOne + NumberTwo + NumberThree;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName()] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
<Window x:Class="StackOverflowTest.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:StackOverflowTest"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="200">
<StackPanel>
<TextBox x:Name="TxtOne" HorizontalAlignment="Stretch" Text="{Binding NumberOne}" Margin="10,10,10,0"/>
<TextBox x:Name="TxtTwo" HorizontalAlignment="Stretch" Text="{Binding NumberTwo}" Margin="10,10,10,0"/>
<TextBox x:Name="TxtThree" HorizontalAlignment="Stretch" Text="{Binding NumberThree}" Margin="10,10,10,0"/>
<TextBlock HorizontalAlignment="Center" Text="{Binding NumberSum}" Margin="10,10,10,0"/>
</StackPanel>
</Window>
Solution two
using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Data;
using System.Collections.Generic;
namespace StackOverflowTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new SimpleMath();
}
}
public class SimpleMath : INotifyPropertyChanged
{
private int _numberOne;
public int NumberOne
{
get => _numberOne;
set
{
_numberOne = value;
OnPropertyChanged();
}
}
private int _numberTwo;
public int NumberTwo
{
get => _numberTwo;
set
{
_numberTwo = value;
OnPropertyChanged();
}
}
private int _numberThree;
public int NumberThree
{
get => _numberThree;
set
{
_numberThree = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName()] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class SumConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var numbers = new List<int>();
foreach (var item in values)
{
if(item is string stringValue)
{
try
{
numbers.Add(System.Convert.ToInt32(stringValue));
}
catch (Exception)
{
Console.WriteLine("Oops, not a number...");
}
}
}
return numbers.Sum().ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}
<Window x:Class="StackOverflowTest.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:StackOverflowTest"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="200">
<StackPanel>
<TextBox x:Name="TxtOne" HorizontalAlignment="Stretch" Text="{Binding NumberOne}" Margin="10,10,10,0"/>
<TextBox x:Name="TxtTwo" HorizontalAlignment="Stretch" Text="{Binding NumberTwo}" Margin="10,10,10,0"/>
<TextBox x:Name="TxtThree" HorizontalAlignment="Stretch" Text="{Binding NumberThree}" Margin="10,10,10,0"/>
<TextBlock HorizontalAlignment="Center" Margin="10,10,10,0">
<TextBlock.Resources>
<local:SumConverter x:Key="SumConverter"/>
</TextBlock.Resources>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource SumConverter}">
<Binding Path="Text" ElementName="TxtOne" />
<Binding Path="Text" ElementName="TxtTwo" />
<Binding Path="Text" ElementName="TxtThree" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Window>
Here's how it looks
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.
I'm having a seriously hard time wrapping my head around the logic in the tutorials and posts about this subject. I'm trying to implement it in a wpf application I'm writing.
Basically, I'm using a listbox to display a ToString of objects in a list and allowing users to add and remove from that list and its corresponding listbox via an add and remove button. The problem I'm having is with the implementation of the Remove button. I want the button disabled if no listbox item is selected, which is one of the things that this pattern is good for. I'm lost as to how to implement that condition.
At the moment, the button is not enabling when I highlight a listbox item. I suppose the CanExecuteChanged event isn't firing.. How do I need to change this?
my CommandsHandler class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace TechopsTools
{
public class CommandHandler : ICommand
{
private Action<object> _execute;
private bool _canExecute;
public CommandHandler(Action<object> execute)
: this(execute, true)
{
}
public CommandHandler(Action<object> execute, bool canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}
my viewmodel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using ProtoBuf;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace TechopsTools
{
class LogCheckClientViewModel : INotifyPropertyChanged
{
private string uri;
private string response;
private bool _canRemove;
private LogConstraints selectedConstraints;
private ObservableCollection<LogConstraints> constraints;
public event PropertyChangedEventHandler PropertyChanged;
public LogConstraints SelectedConstraints
{
get
{
return selectedConstraints;
}
set
{
selectedConstraints = value;
OnPropertyChanged("SelectedConstraints");
}
}
private CommandHandler removeItemCommand;
public ICommand RemoveItemCommand
{
get
{
if (removeItemCommand == null)
removeItemCommand = new CommandHandler(param => RemoveConstraint(), SelectedConstraints != null);
return removeItemCommand;
}
}
public string Response
{
get
{
return response;
}
set
{
response = value;
OnPropertyChanged("Response");
}
}
public string Uri
{
get
{
return uri;
}
set
{
uri = value;
OnPropertyChanged("Uri");
}
}
public ObservableCollection<LogConstraints> Constraints
{
get
{
return constraints;
}
set
{
constraints = value;
OnPropertyChanged("Constraints");
}
}
public LogCheckClientViewModel()
{
constraints = new ObservableCollection<LogConstraints>();
}
public void AddConstraint()
{
NewConstraint newConstraint = new NewConstraint();
newConstraint.ShowDialog();
if (newConstraint._vm.constraint != null)
{
constraints.Add(newConstraint._vm.constraint);
}
}
private void RemoveConstraint()
{
Constraints.Remove(SelectedConstraints);
OnPropertyChanged("Constraints");
}
xaml:
<Window x:Class="TechopsTools.LogCheckClient"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TechopsTools"
Title="LogCheck" Height="453.057" Width="495.986">
<Grid>
<TextBox Text="{Binding Response}" HorizontalAlignment="Left" Height="128" Margin="38,212,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="413" VerticalScrollBarVisibility="Auto" IsEnabled="False"/>
<Label Content="Response" HorizontalAlignment="Left" Margin="38,188,0,0" VerticalAlignment="Top" Width="78" Height="24"/>
<TextBox Text="{Binding Uri}" HorizontalAlignment="Left" Height="23" Margin="38,26,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="413"/>
<Label Content="Uri" HorizontalAlignment="Left" Margin="38,0,0,0" VerticalAlignment="Top" Width="78" Height="24"/>
<Button Content="Add Constraint" HorizontalAlignment="Left" Margin="38,54,0,0" VerticalAlignment="Top" Width="127" Height="56" Click="Add_Click"/>
<Button x:Name="Submit" Content="Submit Request" HorizontalAlignment="Left" Margin="38,345,0,0" VerticalAlignment="Top" Width="413" Height="70" Click="Submit_Click"/>
<ListBox SelectedItem="{Binding Path=SelectedConstraints,UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Constraints}" HorizontalAlignment="Left" Height="124" Margin="182,54,0,0" VerticalAlignment="Top" Width="269">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=description}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Command="{Binding RemoveItemCommand}" Content="Remove Constraint" HorizontalAlignment="Left" Margin="38,122,0,0" VerticalAlignment="Top" Width="127" Height="56" />
</Grid>
</Window>
You really need to be using a CanExecute delegate the same way you're doing an Execute handler.
Basically right now you are checking if it can execute when RemoveItemCommand is first accessed. But then it just keeps that value the entire time.
If you pass in a delegate with that same condition (perhaps adding in for an empty list, not just a null list), I'm betting it'll work.
In other words, in your CommandHandler, change
private bool _canExecute;
to
private Func<bool,object> _canExecute;
and change
public bool CanExecute(object parameter)
{
return _canExecute;
}
to
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
and then in your ViewModel, change
removeItemCommand = new CommandHandler(param => RemoveConstraint(),
SelectedConstraints != null);
to
removeItemcommand = new CommandHandler(param => RemoveConstraint(),
param => SelectedConstraints != null);
(note that this might not be exactly the right code, as I am just writing it freehand, but hopefully you get the point)
I think that thing can be done inside your XAML file, just using DataTrigger.
If this solution would satisfied you, please let me know writing comment, and I will provide you some code.
Best regards