C# WPF MVVM update label with data calculated from textBox - c#

I need to update a label with data calculated from ViewModel. This must be trigged when a textBox is updated because I will use the data from textBox to calcualate the text that must be showed in the label.
My .xaml files is:
...
<TextBox x:Name="tbSelectedValue"
PreviewTextInput="SelectedValue_PreviewTextInput"
KeyUp="SelectedValue_KeyUp"
Text="{Binding Path=SelectedValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding SelectFileCommand}" />
</TextBox.InputBindings>
</TextBox>
<Label x:Name="lbSelectedFileName"
Content="{Binding Path=SelectedName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
...
My ViewModel File is:
public string SelectedValue
{
get { return selectedValue; }
set { SetProperty(ref selectedValue, value); }
}
public string SelectedName
{
get { return selectedName; }
set { selectedName = value; }
}
internal string GetSelectName()
{
try
{
selectedValue = (selectedValue == "" ? "" : GetFileByNumber(Int32.Parse(selectedValue)).name);
return selectedValue;
}
catch (Exception e)
{
return "Nenhum arquivo encontrado";
}
}
The SelectedValue works, but SelectedName don't work.
I need to call the function GetSelectName when the value in textBox is updated (selectedValue). My function GetSelectName update the selectedName property and it must be updated in the view. But it's not working.
What I must do?

As per #EdPlunkett comments let me put this together and see if it helps.
Modify SelectedName so it can notify property changes.
public string SelectedName
{
get { return selectedName; }
set {
SetProperty(ref selectedName, value);
}
}
Assign the result of the GetSelectName() method to SelectedName
property.
public string SelectedValue
{
get { return selectedValue; }
set
{
if (SetProperty(ref selectedValue, value))
{
//If property value changes, update the name property as well
SelectedName = GetSelectedName();
}
}
}
Does it help?

Related

WPF Combobox not updating in twoways

I am binding a combobox with a varaible on my PLC so when i change the combobox the changed value transmitted to my PLC but when i change on my PLC the combobox wont change even if the field is changed
I put a brakepoint on the field(the one i bind to SelectedItem) to check if it changes and it did but didn't affect the combobox
Here is my ViewModel
List<string> _temperatureList = new List<string> { "80 °C", "100 °C", "120 °C" };
public List<string> TemperatureList
{
get { return _temperatureList; }
}
public string Temperature
{
get
{
return (TemperatureGS != 0) ? string.Format("{0} °C", TemperatureGS) : "80 °C";
}
set
{
TemperatureGS = Convert.ToSByte(value.Replace(" °C", ""));
OnPropertyChanged();
WriteTemperature(TemperatureGS);
}
}
private short _temperature ;
public short TemperatureGS
{
get { return _temperature; }
set { SetProperty(ref _temperature, value); }
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
TemperatureGS = PLCread(0);
OnPropertyChanged(Temperature);
}
My Xaml code
<ComboBox SelectedItem="{Binding Temperature, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged }"
ItemsSource="{Binding TemperatureList }" />

c# uwp textblock text property not updating UI during runtime

I'm selecting an item from a combobox to filter a listview of items. The items contain values, and are displayed in a View depending on the filter selection.
<ComboBox Name="YearComboBox" ItemsSource="{x:Bind StudentEnrollment.Years, Mode=OneWay}" SelectedValue="{x:Bind StudentEnrollment.SelectedYear, Mode=TwoWay}”/>
<ListView ItemsSource="{x:Bind StudentEnrollment.FilteredStudentEnrollments, Mode=OneWay}" SelectedIndex="{x:Bind StudentEnrollment.SelectedIndex, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewModels:StudentViewModel" >
<StackPanel Orientation="Horizontal">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{x:Bind Score01, Mode=OneWay}"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public class StudentEnrollmentViewModel : NotificationBase
{
StudentEnrollment StudentEnrollment;
public StudentEnrollmentViewModel()
{
}
private ObservableCollection<StudentEnrollmentViewModel> _StudentEnrollments;
public ObservableCollection<StudentEnrollmentViewModel> StudentEnrollments
{
get { return _StudentEnrollments; }
set
{
SetProperty(ref _StudentEnrollments, value);
}
}
private ObservableCollection<StudentEnrollmentViewModel> _FilteredStudentEnrollments;
public ObservableCollection<StudentEnrollmentViewModel> FilteredStudentEnrollments
{
get { return _FilteredStudentEnrollments; }
set
{
SetProperty(ref _FilteredStudentEnrollments, value);
}
}
private string _selectedYear;
public string SelectedYear
{
get
{
return _selectedYear;
}
set { SetProperty(ref _selectedYear, value);
{ RaisePropertyChanged(SelectedYear); }
RefreshFilteredStudentEnrollmentData(); }
}
private double _Score01;
public double Score01
{
get
{
_Score01 = FilteredStudentEnrollments.Where(y => y.Year == SelectedYear).Select(s => s.Score01).Sum();
return _Score01;
}
}
private void RefreshFilteredStudentEnrollmentData()
{
var se = from seobjs in StudentEnrollments
where seobjs.Year == SelectedYear
select seobjs;
if (FilteredStudentEnrollments.Count == se.Count()) || FilteredStudentEnrollments == null
return;
FilteredStudentEnrollments = new ObservableCollection<StudentEnrollmentViewModel>(se);
}
As expected I can sum the filtered values, and display the total in a TextBlock text property per listview column when the page loads.
<TextBlock Text="{x:Bind StudentEnrollment.Score01, Mode=OneWay}"/>
The issue's the Textblock UI does not update/display the changing property value in the viewmodel via a combobox selection. I sure I'm missing something, or have some logic backwards, hoping some fresh eyes can help.
I am not sure which class is which, because there seems to be some mix up between StudentEnrollmentViewModel and StudentViewModel, but I think I see the reason for the problem.
The Score01 property is a get-only property (actually you can get rid of the _Score01 field and just return the value). Its value is dependent on the FilteredStudentEnrollments and SelectedYear properties. But the UI does not know that so you have to notify it when these properties change that it should bind a new value of Score01 as well:
private ObservableCollection<StudentEnrollmentViewModel> _FilteredStudentEnrollments;
public ObservableCollection<StudentEnrollmentViewModel> FilteredStudentEnrollments
{
get { return _FilteredStudentEnrollments; }
set
{
SetProperty(ref _FilteredStudentEnrollments, value);
RaisePropertyChanged("Score01");
}
}
private string _selectedYear;
public string SelectedYear
{
get
{
return _selectedYear;
}
set
{
SetProperty(ref _selectedYear, value);
RaisePropertyChanged("Score01");
RefreshFilteredStudentEnrollmentData();
}
}
public double Score01
{
get
{
return FilteredStudentEnrollments.
Where(y => y.Year == SelectedYear).Select(s => s.Score01).Sum();
}
}
Notice I have changed the RaisePropertyChanged call to pass in "Score01", as the method expects the name of the property, whereas you were previously passing the value of the SelectedYear property, which meant the PropertyChanged event executed for something like 2018 instead of the property itself. Also note that SetProperty method internally calls RaisePropertyChanged for SelectedYear property as well.

When Binding how do I convert between datatypes?

I am currently working out how to use XAML and how it interacts with C#. My current challenge is trying to get a textblock to change the text it displays when a checkbox is ticked. This requires the program to take a Bool input (is the box ticked?) and give a string output.
Currently when it runs the layout is correct making me suspect the XAML code is fine however the textblock will only display the "unticked" state whether the checkbox is ticked or not.
I suspect that the issue is between the two methods but I am unable find a solution, any suggestions?
The code in question : C#
public class MainPageViewModel : ViewModelBase
{
//stores value of checkbox
private bool _BoxCheckBool;
//Updates value of _BoxCheckBool
public bool BoxCheckBool
{
set
{
Set(ref _BoxCheckBool, value);
}
}
//stores value (for textblock)
private string _BoxCheckString;
public string BoxCheckString
{
//logic that determines what will be sent to the textblock
get
{
if (_BoxCheckBool == true)
{
_BoxCheckString = "The Box has been checked";
}
else if (_BoxCheckBool == false)
{
_BoxCheckString = "The Box has not been checked";
}
else
{
_BoxCheckString = "ERROR";
}
return _BoxCheckString;
}
set
{
Set(ref _BoxCheckString, value);
}
}
}
The code in question : XAML
<CheckBox x:Name="BoxTest" HorizontalAlignment="Center" Content="Check Box" IsChecked="{Binding BoxCheckBool, Mode=TwoWay}"/>
<TextBlock x:Name="BoxTestOutput" Grid.Row="1" Text="{Binding BoxCheckString, Mode=TwoWay}"/>
You only need raise PropertyChanged event when bool property is changed. UI will be updated automatically
public class MainPageViewModel : ViewModelBase
{
//stores value of checkbox
private bool _BoxCheckBool;
//Updates value of _BoxCheckBool
public bool BoxCheckBool
{
set
{
Set(ref _BoxCheckBool, value);
// Assumes your ViewModelBase have method to raising this
RaisePropertyChanged("BoxCheckString");
}
}
private string _BoxCheckString;
public string BoxCheckString
{
//logic that determines what will be sent to the textblock
get
{
if (_BoxCheckBool == true)
{
_BoxCheckString = "The Box has been checked";
}
else
{
_BoxCheckString = "The Box has not been checked";
}
// Boolean type have only two value (true/false)
// - you don't need checking for "errors"
return _BoxCheckString;
}
set
{
Set(ref _BoxCheckString, value);
}
}
}
The text block did not updated because there was no notification when the value was changed.
To make a binding update, you need to implement property change notification (Please check out this link). You need to call something like OnPropertyChanged("BoxCheckString") (depending on your ViewModelBase) inside the setter of BoxCheckBool, such that the text block knows that update is needed.
You are changing the text value is wrong place:
ViewModel:
private bool _BoxCheckBool;
//Updates value of _BoxCheckBool
public bool BoxCheckBool
{
get
{
return _BoxCheckBool;
}
set
{
_BoxCheckBool = value;
OnPropertyChanged("BoxCheckBool");
if (_BoxCheckBool == true)
{
BoxCheckString = "The Box has been checked";
}
else if (_BoxCheckBool == false)
{
BoxCheckString = "The Box has not been checked";
}
else
{
BoxCheckString = "ERROR";
}
}
}
//stores value (for textblock)
private string _BoxCheckString = "";
public string BoxCheckString
{
//logic that determines what will be sent to the textblock
get
{
return _BoxCheckString;
}
set
{
_BoxCheckString = value;
OnPropertyChanged("BoxCheckString");
}
}
Xaml:
<StackPanel Orientation="Horizontal">
<CheckBox Name="BoxTest" IsChecked="{Binding BoxCheckBool, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" HorizontalAlignment="Center" />
<ContentPresenter Content="{Binding BoxCheckString, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>

c# wpf Combox value in editable field must be different that displaymemberpath

I have a Combobox with a concate string in displaymemberpath ('DescriptionComplete' in code-behind below), and that is what i get in the editable field BUT this is only the IdEchantillon that i want into the editable field...
How must i do please ?
XAML:
<ComboBox x:Name="cbEchantillon" SelectedValue="{Binding CurrentEchantillon.IdEchantillon}" ItemsSource="{Binding OcEchantillon}" DisplayMemberPath="DescriptionComplete" SelectedValuePath="IdEchantillon" SelectedItem="{Binding CurrentEchantillon}" Text="{Binding IdEchantillon}" IsEditable="True" Width="355" FontSize="14" FontFamily="Courier New" SelectionChanged="cbEchantillon_SelectionChanged"></ComboBox>
Code behind:
public class Echantillon : ViewModelBase
{
private string _IdEchantillon;
private string _Description;
private DateTime _dateechan;
private string _descriptionComplete;
}
public string IdEchantillon
{
get { return _IdEchantillon; }
set { _IdEchantillon = value; RaisePropertyChanged("IdEchantillon"); }
}
public string Description
{
get { return _Description; }
set { _Description = value; RaisePropertyChanged("Description"); }
}
public DateTime Dateechan
{
get { return _dateechan; }
set { _dateechan = value; RaisePropertyChanged("Dateechan"); }
}
public string DescriptionComplete
{
get { return string.Format("{0} {1} {2}", IdEchantillon.PadRight(20), Dateechan.ToShortDateString(), Description); }
set { _descriptionComplete = value; }
}
}
At first, please, cleanup your ComboBox property bindings. You should not create binding for SelectedValue, because you already have binding for SelectedItem property. The SelectedValue is determined by extracting the value by SelectedValuePath from the SelectedItem.
In case of binding to ViewModel, you should not also bind Text property. Use ItemsSource as you do, and set DisplayMemberPath property to what you want to show as the text representation of the every item in the bound collection.

WPF MVVM Editable Combobox new value is null

Tried all the solutions for similar issues around here, still no go. I have a ComboBox that should work for selection of existing items and/or for adding new ones. Only the selected item part works. Category is just an object with a Name and Id.
Thanks in advance!
XAML
<ComboBox Name="CbCategory" ItemsSource="{Binding Categories}"
SelectedItem="{Binding SelectedCategory.Name, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding NewCategory.Name}" DisplayMemberPath="Name"
IsEditable="True"/>
Code behind
private Category _selectedCategory;
public Category SelectedCategory
{
get { return _selectedCategory; }
set
{
if (Equals(_selectedCategory, value)) return;
_selectedCategory = value;
SendPropertyChanged("SelectedCategory");
}
}
private Category _newCategory;
public Category NewCategory
{
get { return _newCategory; }
set
{
if (Equals(_newCategory, value)) return;
_newCategory = value;
SendPropertyChanged("NewCategory");
}
}
Your Text Binding doesn't work because you're binding against a null Category property. Instantiate it instead.
public Category NewCategory
{
get { return _newCategory ?? (_newCategory = new Category()); }
set
{
if (Equals(_newCategory, value)) return;
_newCategory = value;
SendPropertyChanged("NewCategory");
}
}
Edit: Elaborating as per your comment:
Your ComboBox.Text binding is set to "{Binding NewCategory.Name}", so no matter what the value of SelectedCategory is, the Text property will always reflect the name of the NewCategory.
When NewCategory is null, the Text property has nothing to bind to, and therefore the 2-way binding cannot be executed (that is, the value of the Text property cannot be passed back to NewCategory.Name, because that will cause an NullReferenceException (because the NewCategory is null).
This does not affect the case of the SelectedItem, because that's binding directly to the SelectedCategory property, and not a sub-property of that.
Create new varible to keep text of combobox. If the selectedItem having null value get the text of combobox as new Item,
Code :
<ComboBox Name="CbCategory" ItemsSource="{Binding Categories}"
SelectedItem="{Binding SelectedCategory.Name, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Name}" DisplayMemberPath="Name"
IsEditable="True"/>
private String _name;
public Category Name
{
get { return _name; }
set
{
_name = value
SendPropertyChanged("Name");
}
}
public ICommand ItemChange
{
get
{
`return new RelayCommand(() =>`{
try{string item = this.SelectedCategory.Code;}
catch(Exception ex){string item = this.Name;}
}, () => { return true; });
}
}

Categories