WPF Bind command property from selected item in observablecollection - c#

I am using Listview having datasource as observablecollection<empclass>
My overall class structure is like
Class empclass
{
command = new RelayCommand(myfunction, true);
private int _abc;
public int abc
{
get { return _abc;}
set { _abc = value;
onpropertychanged("abc")
}
private int _pqr;
public int pqr
{
get { return _pqr;}
set { _pqr = value;
onpropertychanged("pqr")
}
public void myfunction()
{
messagebox.show((abc+pqr).Tostring());
}
}
i have a separate button, where on click i want to invoke command of selected item to show addition of abc and pqr on respected values present in that object.
It would be great If you could help me with small code example.
Thanks
Ashpak

I'm assuming you have a ListView named lv:
<ListView Name="lv" ... </ListView>
Then you can bind to the SelectedItem of that ListView and use the item's command property.
<Button Command="{Binding ElementName=lv, Path=SelectedItem.command}">Button Text</Button>
Note that for this to work you have to have a public property command, e.g.:
Class empclass
{
public RelayCommand command {get;set;}
...
}

Try this:
1. XAML code:
<Window x:Class="SoButtonBindingHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:soButtonBindingHelpAttempt="clr-namespace:SoButtonBindingHelpAttempt"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<soButtonBindingHelpAttempt:MainViewModel/>
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding ObservableCollection}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type soButtonBindingHelpAttempt:Empclass}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"></ColumnDefinition>
<ColumnDefinition Width="120"></ColumnDefinition>
<ColumnDefinition Width="120"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding abc, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></TextBlock>
<TextBlock Grid.Column="1" Text="{Binding pqr, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></TextBlock>
<Button Grid.Column="2" Command="{Binding Command}" Content="Press me!"></Button>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid></Window>
2. ViewModel code Model code:
public class MainViewModel:BaseObservableObject
{
public MainViewModel()
{
ObservableCollection = new ObservableCollection<Empclass>(new List<Empclass>
{
new Empclass{abc=2, pqr = 3},
new Empclass{abc=5, pqr = 7},
new Empclass{abc=11, pqr = 13},
new Empclass{abc=17, pqr = 19}
});
}
public ObservableCollection<Empclass> ObservableCollection { get; set; }
}
public class Empclass : BaseObservableObject
{
private ICommand _command;
private int _abc;
private int _pqr;
public ICommand Command
{
get { return _command ?? (_command = new RelayCommand(myfunction)); }
}
public int abc
{
get { return _abc; }
set
{
_abc = value;
OnPropertyChanged("abc");
}
}
public int pqr
{
get { return _pqr; }
set
{
_pqr = value;
OnPropertyChanged("pqr");
}
}
private void myfunction()
{
//add you command logic here
var temp = pqr;
pqr = abc;
abc = temp;
}
}
3. MVVM parts implementation:
public class BaseObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
{
var propName = ((MemberExpression)raiser.Body).Member.Name;
OnPropertyChanged(propName);
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(name);
return true;
}
return false;
}
}
public class RelayCommand<T> : ICommand
{
readonly Action<T> _execute;
readonly Func<T, bool> _canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public void RefreshCommand()
{
var cec = CanExecuteChanged;
if (cec != null)
cec(this, EventArgs.Empty);
}
public bool CanExecute(object parameter)
{
if (_canExecute == null) return true;
return _canExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
}
public class RelayCommand : RelayCommand<object>
{
public RelayCommand(Action execute, Func<bool> canExecute = null)
: base(_ => execute(),
_ => canExecute == null || canExecute())
{
}
}
4. Look like this:
Regards

Related

How to update/convert mumeric TextBox value when changing value's unit using a ComboBox? Value normalization based on current unit?

I would like to have a converter system for my Xamarin and WPF project. I don't want to save any units in the database, so I want directly convert the textbox-values when user change the unit.
I made public a few Observable Collections like;
public class AreaList : ObservableCollection<Unit>
{
public AreaList() : base()
{
Add(new Unit("mm²"));
Add(new Unit("cm²"));
Add(new Unit("dm²"));
Add(new Unit("m²"));
}
}
public class Unit
{
private string name;
public Unit(string name)
{
this.name = name;
}
public string Name
{
get { return name; }
set { name = value; }
}
}
In the View i bind the collection to my combo box. I gave my TextBox the name of his binding property(Text="{Binding TxtBoxValue}" => x:Name="TxtBoxValue"). The ConvertUnitValueCommand set this name as a string in the view model to know which variable the converter function should use when the unit is changed.
View
<UserControl.Resources>
<c:AreaList x:Key="AreaListData" />
</UserControl.Resources>
<TextBox x:Name="TxtBoxValue"
Text="{Binding Mode=TwoWay, Path=TxtBoxValue, UpdateSourceTrigger=PropertyChanged}">
</TextBox>
<ComboBox IsSynchronizedWithCurrentItem="True"
IsEditable="False"
DisplayMemberPath="Name"
SelectedItem="{Binding Unit,Mode=OneWayToSource}"
ItemsSource="{Binding Source={StaticResource AreaListData}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding ConvertUnitValueCommand}"
CommandParameter="{Binding ElementName=TxtBoxValue, Path=Name}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
ViewModel
private string ConvertControlName;
private void ConvertUnitValue(object obj)
{
ConvertControlName = obj.ToString();
}
public Unit Unit
{
get => Get<Unit>();
set
{
if (ConvertControlName != null)
{
FieldInfo variable = this.GetType().GetField(ConvertControlName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
//Get the Value from setted Binding Variable
double oldValue = (double)variable.GetValue(this);
//Convert the value
if (oldValue > 0)
{
double newValue = Converts.ConvertUnitValue(Unit, value, oldValue);
variable.SetValue(this, newValue);
}
Set(value);
}
}
Maybe anyone can give me some inspiration to do it better.
The following example normalizes the user input to the base unit m²:
Unit.cs
public class Unit
{
public Unit(string name, decimal baseFactor)
{
this.Name = name;
this.BaseFactor = baseFactor;
}
#region Overrides of Object
/// <inheritdoc />
public override string ToString() => this.Name;
#endregion
public string Name { get; set; }
public decimal BaseFactor { get; set; }
}
ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
this.Units = new List<Unit>()
{
new Unit("mm²", (decimal) (1 / Math.Pow(1000, 2))),
new Unit("cm²", (decimal) (1 / Math.Pow(100, 2))),
new Unit("dm²", (decimal) (1 / Math.Pow(10, 2))),
new Unit("m²", 1)
};
}
private void NormalizeValue()
{
this.NormalizedValue = this.UnitValue * this.SelectedUnit.BaseFactor;
}
private List<Unit> units;
public List<Unit> Units
{
get => this.units;
set
{
this.units = value;
OnPropertyChanged();
}
}
private Unit selectedUnit;
public Unit SelectedUnit
{
get => this.selectedUnit;
set
{
this.selectedUnit = value;
OnPropertyChanged();
NormalizeValue();
}
}
private decimal unitValue;
public decimal UnitValue
{
get => this.unitValue;
set
{
this.unitValue = value;
OnPropertyChanged();
NormalizeValue();
}
}
private decimal normalizedValue;
public decimal NormalizedValue
{
get => this.normalizedValue;
set
{
this.normalizedValue = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ManiWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DatContext>
<StackPanel>
<!-- Input -->
<TextBox Text="{Binding UnitValue}" />
<ComboBox ItemsSource="{Binding Units}"
SelectedItem="{Binding SelectedUnit}" />
<TextBlock Text="{Binding NormalizedValue}" />
</StackPanel>
</Window>
Reusable solution
A reusable solution would be to create a custom control, which derives from TextBox and encapsulates the normalization logic and the control design.
The following custom control NormalizingNumericTextBox extends TextBox and converts two way from non-normalized value to normalized and back.
It is basically a TextBox aligned with a ComboBox as Unit selector.
It may not be perfect, but it is ready to use and it just took me about 10 minutes to merge the previous answer into this custom control.
NormalizingNumericTextBox supports any type of unit describing a numeric value.
Just bind the NormalizingNumericTextBox.Units property to collection of any kind of Unit implementation e.g. weight, length, currency, etc.
Bind to NormalizingNumericTextBox.NormalizedValue to get/set the normalized value. Setting this property will convert the value to the current NormalizingNumericTextBox.SelectedUnit.
Bind to NormalizingNumericTextBox.Text for the raw input value.
Ensure that the default Style (see below) is added to the ResourceDictionary inside /Themes/Generic.xaml. Customize this Style to customize appearance.
ManiWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DatContext>
<StackPanel>
<!-- Input -->
<NormalizingUnitTextBox NormalizedValue="{Binding NormalizedValue}"
Units="{Binding Units}"
Width="180" />
<!--
Test to show/manipulate current normalized value of the view model.
An entered normalized value will be converted back to the current NormalizingNumericTextBox.Unit -->
<TextBox Background="Red" Text="{Binding NormalizedUnitValue}"/>
</StackPanel>
</Window>
Unit.cs
public class Unit
{
public Unit(string name, decimal baseFactor)
{
this.Name = name;
this.BaseFactor = baseFactor;
}
#region Overrides of Object
/// <inheritdoc />
public override string ToString() => this.Name;
#endregion
public string Name { get; set; }
public decimal BaseFactor { get; set; }
}
ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
this.Units = new List<Unit>()
{
new Unit("m²", 1),
new Unit("dm²", (decimal) (1/Math.Pow(10, 2))),
new Unit("cm²", (decimal) (1/Math.Pow(100, 2))),
new Unit("mm²", (decimal) (1/Math.Pow(1000, 2)))
};
}
public List<Unit> Units { get; set; }
private decimal normalizedValue;
public decimal NormalizedValue
{
get => this.normalizedValue;
set
{
this.normalizedValue = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
NormalizingNumericTextBox.cs
[TemplatePart(Name = "PART_UnitsItemsHost", Type = typeof(ItemsControl))]
public class NormalizingNumericTextBox : TextBox
{
public static readonly DependencyProperty UnitsProperty = DependencyProperty.Register(
"Units",
typeof(IEnumerable<Unit>),
typeof(NormalizingNumericTextBox),
new PropertyMetadata(default(IEnumerable<Unit>), NormalizingNumericTextBox.OnUnitsChanged));
public IEnumerable<Unit> Units
{
get => (IEnumerable<Unit>) GetValue(NormalizingNumericTextBox.UnitsProperty);
set => SetValue(NormalizingNumericTextBox.UnitsProperty, value);
}
public static readonly DependencyProperty SelectedUnitProperty = DependencyProperty.Register(
"SelectedUnit",
typeof(Unit),
typeof(NormalizingNumericTextBox),
new FrameworkPropertyMetadata(
default(Unit),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
NormalizingNumericTextBox.OnSelectedUnitChanged));
public Unit SelectedUnit
{
get => (Unit) GetValue(NormalizingNumericTextBox.SelectedUnitProperty);
set => SetValue(NormalizingNumericTextBox.SelectedUnitProperty, value);
}
public static readonly DependencyProperty NormalizedValueProperty = DependencyProperty.Register(
"NormalizedValue",
typeof(decimal),
typeof(NormalizingNumericTextBox),
new FrameworkPropertyMetadata(
default(decimal),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
NormalizingNumericTextBox.OnNormalizedValueChanged));
public decimal NormalizedValue
{
get => (decimal) GetValue(NormalizingNumericTextBox.NormalizedValueProperty);
set => SetValue(NormalizingNumericTextBox.NormalizedValueProperty, value);
}
private ItemsControl PART_UnitsItemsHost { get; set; }
private bool IsNormalizing { get; set; }
static NormalizingNumericTextBox()
{
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
typeof(NormalizingNumericTextBox),
new FrameworkPropertyMetadata(typeof(NormalizingNumericTextBox)));
}
public NormalizingNumericTextBox()
{
}
private static void OnNormalizedValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var _this = d as NormalizingNumericTextBox;
_this.ConvertNormalizedValueToNumericText();
}
private static void OnSelectedUnitChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as NormalizingNumericTextBox).NormalizeNumericText();
}
private static void OnUnitsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var _this = d as NormalizingNumericTextBox;
_this.SelectedUnit = _this.Units.FirstOrDefault();
}
/// <inheritdoc />
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.PART_UnitsItemsHost = GetTemplateChild("PART_UnitsItemsHost") as ItemsControl;
if (this.PART_UnitsItemsHost == null)
{
throw new InvalidOperationException($"{nameof(this.PART_UnitsItemsHost)} not found in ControlTemplate");
}
this.PART_UnitsItemsHost.SetBinding(
Selector.SelectedItemProperty,
new Binding(nameof(this.SelectedUnit)) {Source = this});
this.PART_UnitsItemsHost.SetBinding(
ItemsControl.ItemsSourceProperty,
new Binding(nameof(this.Units)) {Source = this});
this.SelectedUnit = this.Units.FirstOrDefault();
}
#region Overrides of TextBoxBase
/// <inheritdoc />
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
if (this.IsNormalizing)
{
return;
}
NormalizeNumericText();
}
/// <inheritdoc />
protected override void OnTextInput(TextCompositionEventArgs e)
{
// Suppress non numeric characters
if (!decimal.TryParse(e.Text, NumberStyles.Number, CultureInfo.CurrentCulture, out decimal _))
{
e.Handled = true;
return;
}
base.OnTextInput(e);
}
#endregion Overrides of TextBoxBase
private void NormalizeNumericText()
{
this.IsNormalizing = true;
if (decimal.TryParse(this.Text, NumberStyles.Number, CultureInfo.CurrentCulture, out decimal numericValue))
{
this.NormalizedValue = numericValue * this.SelectedUnit.BaseFactor;
}
this.IsNormalizing = false;
}
private void ConvertNormalizedValueToNumericText()
{
this.IsNormalizing = true;
decimal value = this.NormalizedValue / this.SelectedUnit.BaseFactor;
this.Text = value.ToString(CultureInfo.CurrentCulture);
this.IsNormalizing = false;
}
}
Generic.xaml
<ResourceDictionary>
<Style TargetType="NormalizingNumericTextBox">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="DarkGray" />
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:NormalizingNumericTextBox">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ScrollViewer x:Name="PART_ContentHost" Grid.Column="0" Margin="0" />
<ComboBox x:Name="PART_UnitsItemsHost" Grid.Column="1" BorderThickness="0" HorizontalAlignment="Right" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
I have not much idea about your code impact but I would suggest you try below design which uses MVVM Pattern which removes tight coupling between UI and Backend.
I have separate out the things here
your XAML will have code like
<TextBox x:Name="unitTextbox"
Text="{Binding Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</TextBox>
<ComboBox IsSynchronizedWithCurrentItem="True"
IsEditable="False"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedUnit}"
ItemsSource="{Binding AvailableUnits}">
</ComboBox>
Your ViewModel will be like
public class MainVm : Observable
{
#region Private Fields
private double _value;
private ObservableCollection<Unit> _availableUnits;
private Unit _selectedUnit;
private Unit _previouslySelected;
#endregion Private Fields
#region Public Constructors
public MainVm()
{
_availableUnits = new ObservableCollection<Unit>()
{
new Unit("mm²"),
new Unit("cm²"),
new Unit("dm²"),
new Unit("m²")
};
}
#endregion Public Constructors
#region Public Properties
public double Value
{
get
{
return _value;
}
set
{
if (_value != value)
{
_value = value;
OnPropertyChanged();
}
}
}
public Unit SelectedUnit
{
get { return _selectedUnit; }
set
{
_previouslySelected = _selectedUnit;
_selectedUnit = value;
// call to value conversion function
// convert cm² to mm² or anything
Value = UnitConvertor.Convert(_value, _previouslySelected.Name, _selectedUnit.Name);
OnPropertyChanged();
}
}
public ObservableCollection<Unit> AvailableUnits => _availableUnits;
#endregion Public Properties
}
My Observable class will be like
public class Observable : INotifyPropertyChanged
{
#region Public Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion Public Events
#region Protected Methods
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion Protected Methods
}
better to use an enum for units

Cannot update ListView using ObservableCollection methods via a derived class

There are two views, the first view contains a ListView along with 2 buttons for adding and removing a person. View 2 shows the selected view by using a ContentControl, when the Add button is clicked it shows a form with the name, gender, …, and a button to store the information and display the person name in the ListView.
When I try to remove a person from the ListView it works since the button is within the same view/context of the ListView. But when I call the Add method via AddViewModel it does not update the UI.
Below it is available the relevant parts code of the different classes.
View sample
View1.xaml
<Window.Resources>
<vm:ViewModelBase x:Key="ViewModel"/>
<DataTemplate x:Name="addViewTemplate" DataType="{x:Type vm:AddViewModel}">
<views:AddView/>
</DataTemplate>
</Window.Resources>
<Button Command="{Binding change2addViewCommand,
Source={StaticResource ViewModel}}">
<materialDesign:PackIcon Kind="Plus" Width="12"/>
</Button>
<Button Command="{Binding removeFromListCommand, Source={StaticResource ViewModel}}"
CommandParameter="{Binding ElementName=lvNames, Path=SelectedItem}">
<materialDesign:PackIcon Kind="Delete" Width="12"/>
</Button>
<ListView MinHeight="560"
DataContext="{Binding Source={StaticResource ViewModel}}"
ItemsSource="{Binding}"
x:Name="lvNames" >
</ListView>
<ContentControl x:Name="controllerViews" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="4"
Grid.RowSpan="5" Content="{Binding CurrentView, Mode=TwoWay}"/>
ViewModelBase.cs
public class ViewModelBase: ObservableCollection<Person>, INotifyPropertyChanged
{
private ViewModelBase currentView;
public ViewModelBase CurrentView
{
get
{
return currentView;
}
set
{
currentView = value;
OnPropertyChanged("CurrentView");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public ViewModelBase()
{
for (int i = 1; i <= 2; i++)
{
Add(new Person()
{
Name = "Person" + i,
Age = i,
Gender = Gender.Female,
Comments = "",
Photo_Path = ""
});
}
change2addViewCommand = new RelayCommand(ExecuteAddCommand, CanExectuteAddCommand);
removeFromListCommand = new RelayCommand(ExecuteRemoveCommand, CanExectuteRemoveCommand);
}
public event PropertyChangedEventHandler PropertyChanged;
public bool CanExectuteAddCommand(object param)
{
return true;
}
public void ExecuteAddCommand(object param)
{
CurrentView = new AddViewModel();
}
public bool CanExectuteRemoveCommand(object param)
{
return true;
}
public void ExecuteRemoveCommand(object param)
{
Person player = param as Person;
Remove(player); //Working
}
protected void AddToListView(Person player)
{
Add(player) // Not Working
}
}
}
AddViewModel.cs
public class AddViewModel: ViewModelBase
{
public RelayCommand addViewCheckCommand { get; set; }
public AddViewModel()
{
addViewCheckCommand = new RelayCommand(ExecuteCheckCommand, CanExecuteCheckCommand);
}
private bool CanExecuteCheckCommand(object param)
{
return true;
}
private void ExecuteCheckCommand(object param)
{
BocciaPlayer player = param as Person;
AddToListView(player); // Inherited method
}
}

ICommand interface not works

I have problem with navigation after I have added ICommand interface. Button is still disable and I don't why. For me everythings looks good but if I type in text into Textbox. Maybe binding doesn't work and i don't have value on out. I have tried figure out what's wrong with that code but I don't have idea
RegisterVM
public class RegisterVM : INotifyPropertyChanged
{
// string connetionString = null;
// string sql = null;
private string _social;
public string Social
{
get { return _social; }
set
{
_social = value;
Users = new Users()
{
Name = this.Name,
Surname = this.Surname,
Social = this.Social,
};
OnPropertyChanged("Social");
}
}
private string _surname;
public string Surname
{
get { return _surname; }
set
{
_surname = value;
Users = new Users()
{
Name = this.Name,
Surname = this.Surname,
Social = this.Social,
};
OnPropertyChanged("Surname");
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
Users = new Users()
{
Name = this.Name,
Surname = this.Surname,
Social = this.Social,
};
OnPropertyChanged("Name");
}
}
private Users _user;
public Users Users
{
get { return _user; }
set
{
_user = value;
OnPropertyChanged("Users");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public NavigationCandidates navigationCandidates { get; set; }
private readonly MainWindow _mainWindow;
public RegisterVM(MainWindow mainWindow)
{
_mainWindow = mainWindow;
navigationCandidates = new NavigationCandidates(this);
}
public void Navigate()
{
_mainWindow.Content = new Candidates();
_mainWindow.name.Text = string.Empty;
_mainWindow.Surname.Text = string.Empty;
_mainWindow.SearchTermTextBox.Text = string.Empty;
}
public void Register(Users user)
{
Users.Register(user);
}
}
}
Users
public class Users : INotifyPropertyChanged
{
private string _id;
public string Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
private string _surname;
public string Surname
{
get { return _surname; }
set
{
_surname = value;
OnPropertyChanged("Surname");
}
}
private string _social;
public string Social
{
get { return _social; }
set
{
_social = value;
OnPropertyChanged("Social");
}
}
private bool _voted;
public bool Voted
{
get { return _voted; }
set
{
_voted = value;
OnPropertyChanged("Voted");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
string connetionString = null;
string sql = null;
public void Register(Users User)
{
connetionString = "Data Source=(LocalDB)\\MSSQLLocalDB;AttachDbFilename=C:\\Users\\Anon\\Documents\\Db.mdf;Integrated Security=True;Connect Timeout=30";
using (SqlConnection cnn = new SqlConnection(connetionString))
{
sql = "insert into Table ([Name],[Surname],[Social]values(#name,#surname,#social)";
cnn.Open();
using (SqlCommand cmd = new SqlCommand(sql, cnn))
{
cmd.Parameters.AddWithValue("#name", User.Name);
cmd.Parameters.AddWithValue("#surname", User.Surname);
cmd.Parameters.AddWithValue("#social", User.Social);
cmd.ExecuteNonQuery();
}
}
}
}
Navigaton
public class NavigationCandidates : ICommand
{
// public CandidatesVM candidatesVM { get; set; }
public RegisterVM regVM { get; set; }
public event EventHandler CanExecuteChanged;
/* public NavigationCandidates(CandidatesVM canVM)
{
candidatesVM = canVM;
}*/
public NavigationCandidates(RegisterVM regiVM)
{
regVM = regiVM;
}
public bool CanExecute(object parameter)
{
Users user = (Users)parameter;
if (user != null)
{
if (string.IsNullOrEmpty(user.Name) || string.IsNullOrEmpty(user.Surname) || string.IsNullOrEmpty(user.Social))
return false;
return true;
}
return false;
}
public void Execute(object parameter)
{
Users user = (Users)parameter;
regVM.Register(user);
regVM.Navigate();
// candidatesVM.Input();
// candidatesVM.Navigate();
// candidatesVM.DeSerializationCamera();
}
}
XAML
<Window.Content>
<Border Background="#202021" CornerRadius="40">
<StackPanel Margin="20">
<Label Content="Type in your data" Foreground="White" HorizontalAlignment="Center" FontSize="20" FontWeight="Bold"/>
<Separator/>
<Label Content="Name" HorizontalAlignment="Center" Foreground="White" FontWeight="Bold"/>
<TextBox HorizontalAlignment="Center" MinWidth="320" MinHeight="32" Foreground="Black" Background="#f5ddff" FontSize="15" FontWeight="DemiBold"
TextAlignment="Center" Margin="0,0,0,10" x:Name="name" Padding="0,4,0,-2" Text="{Binding Name, Mode=TwoWay}" MaxWidth="320" MaxLength="40" PreviewTextInput="SignValidationTextBox"/>
<TextBlock IsHitTestVisible="False" Text="Enter Name" HorizontalAlignment="Center" Margin="0,-35,0,0" Foreground="Black" >
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=name}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Label Content="Surname" HorizontalAlignment="Center" Foreground="White" FontWeight="Bold"/>
<TextBox HorizontalAlignment="Center" MinWidth="320" MinHeight="32" Foreground="Black" Background="#f5ddff" FontSize="15" FontWeight="DemiBold" Margin="0,0,0,10"
x:Name="Surname" TextAlignment="Center" Padding="0,4,0,-2" MaxHeight="320" Text="{Binding Surname, Mode=TwoWay}" MaxLength="40" PreviewTextInput="SignValidationTextBox" MaxWidth="320"/>
<TextBlock IsHitTestVisible="False" Text="Enter Surname" HorizontalAlignment="Center" Margin="0,-35,0,0" Foreground="Black">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=Surname}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Label Content="Social" HorizontalAlignment="Center" Foreground="White" FontWeight="Bold"/>
<TextBox HorizontalAlignment="Center" MinWidth="320" MinHeight="32" Foreground="Black" Background="#f5ddff" FontSize="15" FontWeight="DemiBold" MaxLength="11" Margin="0,0,0,10"
x:Name="SearchTermTextBox" PreviewTextInput="NumberValidationTextBox" Text="{Binding Social, Mode=TwoWay}" TextAlignment="Center" Padding="0,4,0,-2" />
<TextBlock IsHitTestVisible="False" Text="Enter Social" HorizontalAlignment="Center" Margin="0,-35,0,0" Foreground="Black" >
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=SearchTermTextBox}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Separator Margin="0,10,0,0"/>
<Button HorizontalAlignment="Center" MinHeight="32" MinWidth="120" Foreground="Black" Background="White" Margin="0,45,0,0" Content="Login" FontWeight="Bold" BorderThickness="2"
x:Name="BtnClk" Command="{Binding navigationCandidates}" CommandParameter="{Binding Users}"/>
</StackPanel>
</Border>
</Window.Content>
MainPage
public partial class MainWindow : NavigationWindow
{
RegisterVM registerVM;
public MainWindow()
{
InitializeComponent();
registerVM = new RegisterVM(this);
DataContext = registerVM;
}
}
Your problem is here:
public NavigationCandidates navigationCandidates { get; set; }
public RegisterVM(MainWindow mainWindow)
{
_mainWindow = mainWindow;
navigationCandidates = new NavigationCandidates(this);
}
You bind your button to a property that doesn't notify.
TextBox Binding isn't wrong , Only thing that I can't see in your code is ICommand Binding in RegisterVM.
Implement RelayCommand Class in your code and then
add Icommand property in you RegisterVM like this.
private ICommand _navigationCandidates;
public ICommand NavigationCandidates
{
get
{
return _navigationCandidates ?? (_navigationCandidates = new RelayCommand(OnnavigationClicked));
}
}
private void OnnavigationClicked()
{
// throw new NotImplementedException();
}
Ok, here an example
1 - Create a RelayCommand Class
//necessary namespaces
using System;
using System.Diagnostics;
using System.Windows.Input;
public class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#region Constructors
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
//For Vs 2015+
return _canExecute?.Invoke(parameter) ?? true;
}
/// <summary>
/// Can execute changed event handler.
/// </summary>
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
2 - Create a ViewModelBase (preferably abstract class), implement INotifyPropertyChanged
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public virtual ICommand NavigateCommand => new RelayCommand(Navigate, o=> CanExecute());
//You'll override this method in your class
protected virtual bool CanExecute()
{
return true;
}
//You'll override this method in your class
protected virtual void Navigate(object param)
{
}
}
3 - Your ViewModel.
public class ViewModel: ViewModelBase
{
protected override Navigate(object param)
{
//here you navigate
}
protected override bool CanExecute()
{
var result = false;
//do whatever you want to return true or false;
return result;
}
}
note: - you are mixing MVVM with code behind, you could not for example set the values of your TextBoxes directly in the Navigate method.
I suggest you read materials regarding MVVM Pattern
Add a method to your NavigationCandidates class that raises the CanExecuteChanged event:
public class NavigationCandidates : ICommand
{
public RegisterVM regVM { get; set; }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
public NavigationCandidates(RegisterVM regiVM)
{
regVM = regiVM;
}
public bool CanExecute(object parameter)
{
Users user = (Users)parameter;
if (user != null)
{
if (string.IsNullOrEmpty(user.Name) || string.IsNullOrEmpty(user.Surname) || string.IsNullOrEmpty(user.Social))
return false;
return true;
}
return false;
}
public void Execute(object parameter)
{
Users user = (Users)parameter;
regVM.Register(user);
regVM.Navigate();
}
}
...and call this method whenever the Users is set in the RegisterVM class:
public Users Users
{
get { return _user; }
set
{
_user = value;
OnPropertyChanged("Users");
navigationCandidates.RaiseCanExecuteChanged();
}
}
You may also want to set the UpdateSourceTrigger property of the bindings in the view to PropertyChanged for the Button to become enabled as soon as you have typed something into all three TextBoxes:
Text="{Binding Social, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

copy textbox value to another when click on Button Command in WPF MVVM

I have two text box and want to copy first textbox value to another textbox whenever I click on Button and this should done by using Commands in WPF.
This is my scenario :
First textbox binds the value from Person class.
Button shows simple MsgBox which verifies that Command executed properly.
Well here, I want to pass first textbox value to 2nd textbox (using Command) ?
XML File:
<Window x:Class="PrismDemo.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:PrismDemo.ViewModels"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:Person x:Name="vmmmm1" />
</Window.DataContext>
<Grid>
<TextBox x:Name="fName" Grid.Row="1" Height="30" Width="100" Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Name="Submit" Grid.Row="2" Height="30" Width="100" Content="Submit Me" Command="{Binding submitCommand}" CommandParameter="{Binding Text, ElementName=fName}"/>
<TextBox x:Name="display" Grid.Row="3" Height="30" Width="100" Text="{}" />
Person class (ViewModel):
public class Person:INotifyPropertyChanged
{
private string _firstName;
private string _copyName;
public ICommand submitCommand {get;set;}
public Person()
{
_firstName = "Ronaldo";
submitCommand = new RelayCommand(MyMethod, canExecuteMethod);
}
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
OnPropertyUpdated(FirstName);
//OnPropertyUpdated(CopyName);
}
}
public string CopyName
{
get
{
return _copyName;
}
set
{
OnPropertyUpdated(CopyName);
}
}
private void OnPropertyUpdated(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
private bool canExecuteMethod(object parameter)
{
return true;
}
private void MyMethod(object parameter)
{
MessageBox.Show("Welcome to Command Demo...");
//if (parameter == null) return;
//_copyName = parameter.ToString();
this._copyName = _firstName;
}
public event PropertyChangedEventHandler PropertyChanged;
}
Any help will be appreciated.
Thank you !!
You were almost right ....Its working at my place properly , Just make following changes in you code
Just remove command parameter... we dont need it and Bind the copied string.
<TextBox Grid.Row="1" Height="30" Width="100" Text="{Binding FirstName}" />
<Button Grid.Row="2" Height="30" Width="100" Content="Submit Me" Command="{Binding submitCommand}"/>
TextBox Grid.Row="3" Height="30" Width="100" Text="{Binding CopyName}" />
In View model make following changes...
public class Person:INotifyPropertyChanged{
private string _firstName;
private string _copyName=string.Empty;
public Person()
{
_firstName = "Ronaldo";
submitCommand = new RelayCommand(MyMethod, canExecuteMethod);
}
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
public string CopyName
{
get
{
return _copyName;
}
set
{
if (_copyName != value)
{
_copyName = value;
OnPropertyChanged("CopyName");
}
}
}
public ICommand submitCommand { get; set; }
private void MyMethod(object param)
{
MessageBox.Show("Welcome to Command Demo...");
CopyName = FirstName;
}
private bool canExecuteMethod(object parameter)
{
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(params string[] propertyNames)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
foreach (string propertyName in propertyNames) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
handler(this, new PropertyChangedEventArgs("HasError"));
}
}
}
You don't need CommandParameter here.
<Button Name="Submit" Grid.Row="2" Height="30" Width="100" Content="Submit Me" Command="{Binding submitCommand}" />
Add the Display property:
public string Display
{
get
{
return _display;
}
set
{
_display = value;
OnPropertyUpdated(Display);
}
}
Fix the binding in the second TextBox:
<TextBox x:Name="display" Grid.Row="3" Height="30" Width="100" Text="{Binding Display}" />
Update MyMethod:
private void MyMethod(object parameter)
{
MessageBox.Show("Welcome to Command Demo...");
Display = FirstName;
}
Here is how to copy text from one textBox to another.
this is dataContext behinde MainWindow
public class TestVM : INotifyPropertyChanged
{
public TestVM()
{
CopyCommand = new RelayCommand<string>(OnCopyExecuted);
}
private void OnCopyExecuted(string commandParameter)
{
TextUpdate = commandParameter;
}
private string _textUpdate;
public string TextUpdate
{
get { return _textUpdate; }
set
{
if (_textUpdate != value)
{
_textUpdate = value;
OnPropertyChanged();
}
}
}
public RelayCommand<string> CopyCommand { get; private set; }
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Generic RelayCommand that can take parameters
public class RelayCommand<T> : ICommand
{
private Action<T> _executeMethod;
private Func<T, bool> _canExecuteMethod;
#region RelayCommand ctor
public RelayCommand(Action<T> executeMethod)
{
_executeMethod = executeMethod;
}
public RelayCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
{
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
}
#endregion
public void RaiseCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
#region ICommand Members
bool ICommand.CanExecute(object parameter)
{
var Tparam = (T)parameter;
if (_canExecuteMethod != null)
return _canExecuteMethod(Tparam);
if (_executeMethod != null)
return true;
return false;
}
void ICommand.Execute(object parameter)
{
if (_executeMethod != null)
_executeMethod((T)parameter);
}
public event EventHandler CanExecuteChanged = delegate { };
#endregion
}
and MainWindow xaml just to show purpose
<Window.DataContext>
<local:TestVM />
</Window.DataContext>
<Grid>
<TextBox x:Name="txt1"
Height="35"
Width="150"
Margin="49,62,318,224" />
<TextBox Text="{Binding TextUpdate}"
Height="35"
Width="150"
Margin="313,62,54,226" />
<Button Command="{Binding CopyCommand}"
CommandParameter="{Binding ElementName=txt1,Path=Text}"
Content="Copy"
Grid.Row="0"
Margin="208,157,198,132" />
</Grid>
It's working. Now you can implement it as it fits your needs.

PropertyChanged is null wpf

Help me please!!!
I have 3 UserControls
I select user on List Users UC listbox
Then send message from SendMessage UC to Database
when i send message to Db it must refresh my chat listBox in Correspondence UC, but problem is in my ChatWrapper.
PropertyChanged in ChatWrapper is always null, and I can't refresh my ListBox in Correspondence UC with new message
List Users:
public IEnumerable<EmployeesDb> getListNames
{
get { return Db.Instance.EmployeesDbs.ToList(); }
}
static EmployeesDb m_selectedUser;
public static EmployeesDb selectedUser
{
get { return m_selectedUser; }
set
{
if (value != null)
m_selectedUser = value;
Correspondence correspondence = new Correspondence();
correspondence.CorrespondenceChat();
}
}
}
Send Message ( I try to refresh -> SendInfo.FirstOrDefault().RefreshGUI();)
public static DependencyProperty SendInfoProperty =
DependencyProperty.Register(
"SendInfo",
typeof(IEnumerable<ChatWrapper>),
typeof(SendMessage));
public IEnumerable<ChatWrapper> SendInfo
{
get { return GetValue(SendInfoProperty) as IEnumerable<ChatWrapper>; }
set { SetValue(SendInfoProperty, value); }
}
void SendMessageCommandExecute()
{
//...
SendInfo.FirstOrDefault().RefreshGUI();
//...
}
ChatWrapper
public class ChatWrapper : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void FirePropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
public void RefreshGUI()
{
FirePropertyChanged("message");
}
public ChatDb chatDb { get; set; }
public string message
{
get
{
return (chatDb != null) ? string.Format("{0} {1}.{2} / {3} / {4}\n{5}",
chatDb.FromEmployeesDb.surname,
chatDb.FromEmployeesDb.name[0],
chatDb.FromEmployeesDb.middleName[0],
chatDb.messageDateTime,
chatDb.computerName,
chatDb.message) : null;
}
}
Correspondence
//...
public partial class Correspondence : UserControl, INotifyPropertyChanged
{
public static DependencyProperty GetCorrespondenceInfoProperty =
DependencyProperty.Register(
"GetCorrespondenceInfo",
typeof(IEnumerable<ChatWrapper>),
typeof(Correspondence),
new PropertyMetadata(OnChanged));
public IEnumerable<ChatWrapper> GetCorrespondenceInfo
{
get { return GetValue(GetCorrespondenceInfoProperty) as IEnumerable<ChatWrapper>; }
set { SetValue(GetCorrespondenceInfoProperty, value); }
}
static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var me = d as Correspondence;
me.chat = me.GetCorrespondenceInfo;
}
ICollectionView m_CollectionView;
public static IEnumerable<ChatWrapper> m_chat;
public IEnumerable<ChatWrapper> chat
{
get { return m_chat; }
set
{
m_chat = value;
if (ListUsers.selectedUser != null)
CorrespondenceChat();
FirePropertyChanged("chat");
}
}
public void CorrespondenceChat()
{
if (m_chat == null)
return;
m_CollectionView = CollectionViewSource.GetDefaultView(m_chat);
//...
FirePropertyChanged("chat");
}
XAML of Correspondence (refresh
<Grid>
<ListBox x:Name="correspondenceListBox" ItemsSource="{Binding chat, RelativeSource={RelativeSource AncestorType={x:Type local:Correspondence}}}"
Height="auto" Grid.Row="0" Grid.Column="1" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding message}" TextWrapping="Wrap" Width="auto"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I tried to write
public event PropertyChangedEventHandler PropertyChanged = delegate { };
PropertyChanged is no longer null, but it's still not updated

Categories

Resources