Bind UserControl Property to ViewModel - c#

I have an UserContol Combination of Country Code Combobox and A number Textbox.
In my Window, I have 2 UserControl (Primary Phone Number and Secondary Phone Number)
Here is my Model
public class ContactNumber
{
public Country Country { get; set; }
public string Number { get; set; }
}
public class Country
{
public int id { get; set; }
public string name { get; set; }
public string code { get; set; }
public string phone_code { get; set; }
public int is_active { get; set; }
public List<Country> country { get; set; }
}
Here is My UserControl XAML
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="250"></ColumnDefinition>
</Grid.ColumnDefinitions>
<!--ComboBox for Country-->
<ComboBox Grid.Column="0" Style="{StaticResource ComboBoxMerged}" x:Name="cmbCountry"></ComboBox>
<TextBlock Text="|" FontSize="{StaticResource fontSize30}" Foreground="{StaticResource LightSilverBrush}"
HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Column="1" Margin="0,-5,0,0">
</TextBlock>
<!--TextBox for Number-->
<TextBox Style="{StaticResource TextBoxWithDropDown}"
Text="{Binding ContactNumber.Number,ElementName=ContactWindow,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
x:Name="txtContactNumber" TextChanged="txtContactNumber_TextChanged" Width="240" Grid.Column="2">
</TextBox>
</Grid>
Here is my Control Code Behind
public ContactNumber ContactNumber
{
get { return (ContactNumber)GetValue(ContactNumberProperty); }
set
{
SetValue(ContactNumberProperty, value);
}
}
// Using a DependencyProperty as the backing store for ContactNumber. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContactNumberProperty =
DependencyProperty.Register("ContactNumber", typeof(ContactNumber), typeof(ContactNumberWithCountryCode),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
SetText));
private static void SetText(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ContactNumberWithCountryCode contactNumberWithCountryCode=d as ContactNumberWithCountryCode;
if (contactNumberWithCountryCode!=null)
{
contactNumberWithCountryCode.txtContactNumber.Text = (e.NewValue as ContactNumber).Number.ToString();
}
}
public ContactNumberWithCountryCode()
{
InitializeComponent();
}
private void txtContactNumber_TextChanged(object sender, TextChangedEventArgs e)
{
ContactNumber.Number = txtContactNumber.Text;
}
This is my Main Window XAMl
<usercontrols:ContactNumberWithCountryCode ContactNumber="{Binding PrimaryContactNumber,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" x:Name="PracticePhoneNumber" Margin="0,15,0,0" ></usercontrols:ContactNumberWithCountryCode>
<usercontrols:ContactNumberWithCountryCode ContactNumber="{Binding SecondryContactNumber,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" x:Name="SecondaryPracticePhoneNumber" Margin="0,15,0,0" ></usercontrols:ContactNumberWithCountryCode>
Here is my View Model Code
private ContactNumber primaryContactNumber;
public ContactNumber PrimaryContactNumber
{
get { return primaryContactNumber; }
set
{
primaryContactNumber = value;
OnPropertyChanged("PrimaryContactNumber");
}
}
private ContactNumber secondryContactNumber;
public ContactNumber SecondryContactNumber
{
get { return secondryContactNumber; }
set
{
secondryContactNumber = value;
OnPropertyChanged("SecondryContactNumber");
}
}
My goal is to fill ViewModel Property on Text Or Combobox Value Change.
Thanks in Advance.

Related

Xamarin Forms - Change a CheckBox value when other CheckBox changed inside a ListView

I have a ListView with a ItemSource binding to a list object; inside the listview there are some items that are filled with the values of the list object. I have 2 checkboxes that are binding to elements of the list object and want to uncheck one when the other is checked. In the code I wrote, the values are correctly changed in the list object but the checkbox didn't change (stay unchecked). Next is the code that I wrote.
XAML Part
<ListView ItemsSource="{Binding ListaAsistencia}" HasUnevenRows="True" SelectionMode="None" x:Name="AsistList">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame Margin="5,2,5,2" Padding="0" BackgroundColor="Transparent" BorderColor="#915c0d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".7*"></ColumnDefinition>
<ColumnDefinition Width=".3*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Label Text="{Binding alumno, Mode=TwoWay}" Grid.Row="0" Grid.Column="0" FontSize="Micro" Margin="5,2,2,2"/>
<Entry Placeholder="Notas" Text="{Binding notas}" Grid.Row="1" Grid.Column="0" FontSize="Micro" TextColor="Black" />
<Grid Grid.Row="0" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".65*"></ColumnDefinition>
<ColumnDefinition Width=".35*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Text="Asistió" Grid.Column="0" HorizontalOptions="End" FontSize="Micro" VerticalOptions="Center"/>
<CheckBox IsChecked="{Binding asistencia, Mode=TwoWay}" Grid.Column="1" HorizontalOptions="Start" Color="Black"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".65*"></ColumnDefinition>
<ColumnDefinition Width=".35*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Text="F. Just." Grid.Column="0" FontSize="Micro" HorizontalOptions="End" VerticalOptions="Center"/>
<CheckBox IsChecked="{Binding falta_justificada, Mode=TwoWay}" Grid.Column="1" HorizontalOptions="Start" Color="DarkBlue" CheckedChanged="CheckBox_Just_CheckedChanged" AutomationId="{Binding idalumno_grupo}"/>
</Grid>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
C# BackEnd Part
private void CheckBox_Just_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
var vm = BindingContext as AsisCapturarViewModel;
if ((e.Value) && (!vm.obtainingData))
{
CheckBox switchBox = (CheckBox)sender;
vm.UncheckFalta(switchBox.AutomationId);
}
}
C# View Model Part
public async void UncheckFalta(string idalumno_grupo)
{
if (!String.IsNullOrEmpty(idalumno_grupo))
{
int idalumno_grupoUse = Convert.ToInt32(idalumno_grupo);
ListaAsistencia.Where(a => a.idalumno_grupo == idalumno_grupoUse).ToList().ForEach(s => s.asistencia = false);
}
}
Class used in the List
public class AsistenciaList
{
public int idasistencia { get; set; }
public DateTime fecha { get; set; }
public int idtipo_evento { get; set; }
public string tipo_evento { get; set; }
public int idmaestro_grupo { get; set; }
public int idalumno_grupo { get; set; }
public string alumno { get; set; }
public bool asistencia { get; set; }
public string notas { get; set; }
public bool falta_justificada { get; set; }
}
Thanks for your help.
Firstly , agree with Jason .You should implement the interface INotifyPropertyChanged in your model if you want to update UI in runtime .
In addition , since you have used MVVM , you should put all the logic to your viewmodel .
So you can improve your code as following
in your model
public class AsistenciaList:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public int idasistencia { get; set; }
public DateTime fecha { get; set; }
public int idtipo_evento { get; set; }
public string tipo_evento { get; set; }
public int idmaestro_grupo { get; set; }
public int idalumno_grupo { get; set; }
public string alumno { get; set; }
public string notas { get; set; }
private bool asis;
public bool asistencia
{
get
{
return asis;
}
set
{
if (asis != value)
{
asis = value;
NotifyPropertyChanged();
}
}
}
private bool falta;
public bool falta_justificada
{
get
{
return falta;
}
set
{
if (falta != value)
{
falta = value;
NotifyPropertyChanged();
}
}
}
}
xaml
<CheckBox IsChecked="{Binding falta_justificada, Mode=TwoWay}" Grid.Column="1" HorizontalOptions="Start" Color="DarkBlue" AutomationId="{Binding idalumno_grupo}"/>
ViewModel
//...
foreach(AsistenciaList asistencia in ListaAsistencia)
{
asistencia.PropertyChanged += Asistencia_PropertyChanged;
}
//...
private void Asistencia_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName== "falta_justificada")
{
AsistenciaList asistencia = sender as AsistenciaList;
var idalumno_grupo = asistencia.idalumno_grupo;
//...do something you want
}
}

C# datagrid is not updating itemsource when get added

when typing in the textbox and click "Add Employee", i want it to update and display to the datagrid, i've implemented INotifyPropertyChanged and RelayCommand. what am i missing that's not populating the data. thanks in advance
here is my model
public class EmployeeModel
{
public string Name { get; set; }
public int Pedicure { get; set; }
public int Tip { get; set; }
public int Total { get; set; }
}
this is my ViewModel
List<EmployeeModel> employeeModel = new List<EmployeeModel>() { };
private ICommand _addEmployeeCommand;
public ICommand AddEmployeeCommand
{
get
{
return _addEmployeeCommand ?? (_addEmployeeCommand = new RelayCommand(x => { AddNewEmployee(); }));
}
}
public List<EmployeeModel> Employee
{
get { return employeeModel; }
set
{
if(value != employeeModel)
{
employeeModel = value;
OnPropertyChanged("Employee");
}
}
}
private string employeeName;
public string EmployeeName
{
get { return employeeName; }
set
{
if (value != employeeName)
{
employeeName = value;
OnPropertyChanged("EmployeeName");
}
}
}
public void AddNewEmployee()
{
Employee.Add(new EmployeeModel { Name = EmployeeName });
}
here is my View
<TabItem Header="Employee">
<StackPanel Orientation="Vertical">
<DataGrid ItemsSource="{Binding Employee}">
</DataGrid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name: "/>
<TextBox Text="{Binding EmployeeName}"
Width="40"
Height="15"
VerticalAlignment="Top"/>
<Button Content="Add"
Command="{Binding AddEmployeeCommand}"
Height="20"
VerticalAlignment="Top"/>
</StackPanel>
</StackPanel>
(I pluralized the name Employee to Employees in this answer for future readers)
The problem is with the Source of the DataGrid
Bear in mind that OnPropertyChanged("Employees") only notifies about the changes made to the Employees and is not responsible for any changes made within Employees.
To be clear, it only works when you do employeeModels = new List<EmployeeModel>()
And won't be called when Employees.Add(employee)
Hopefully WPF has its own ObservableCollection type that will take care of that:
private ObservableCollection<Employee> _employees = new ObservableCollection<Employee>;
public ObservableCollection<Employee> Employees { get { return _employees; } }

How to add textbox values to list using MVVM?

Models
public class EmployeeDetails
{
public string Name { get; set; }
public int Age {get;set;}
}
public class AddressDetails
{
public EmployeeDetails EmployeeName { get; set; }
public string City { get; set; }
}
View
<Window x:Class="ClassCollection.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:ClassCollection"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding Source={StaticResource loc},Path=ViewModel}"
>
<Grid>
<StackPanel Margin="0 20 0 0">
<TextBox x:Name="txt1" Width="90" Height="20" Text="{Binding Details.EmployeeName}"/>
<TextBox x:Name="txt2" Width="90" Height="20" Text="{Binding Details.City}" Margin="0 20 0 0"/>
</StackPanel>
<Button x:Name="btn" Width="90" Height="25" Content="Add" Command=" {Binding AddCommand}"/>
</Grid>
</Window>
ViewModel
public class Viewmodel
{
public ObservableCollection<AddressDetails> EmployeeList;
public Viewmodel()
{
EmployeeList = new ObservableCollection<AddressDetails>();
LoadCommand();
}
private AddressDetails _details;
public AddressDetails Details
{
get { return _details; }
set
{
_details = value;
}
}
// Commands
public ICommand AddCommand { get; set; }
private void LoadCommand()
{
AddCommand = new CustomCommand(Add, CanAdd);
}
private bool CanAdd(object obj)
{
return true;
}
private void Add(object obj)
{
EmployeeList.Add(new AddressDetails { EmployeeName = Details.EmployeeName, City = Details.City });
}
}
Locator
public class Locator
{
private static Viewmodel viewmodel = new Viewmodel();
public static Viewmodel ViewModel
{
get { return viewmodel; }
}
}
How to add TextBox value to collection list using MVVM?
The Above is my code that I have tried. It shows null reference exception if I do like above. What would be the problem?
Update
I have two fields in EmployeeDetails class. So I must give input for these two field when add to collection. But I need only one field Name to insert to the collection. How to do it?
Analysis
It seems the _details field is not «initialized».
Solution
Please consider introducing the appropriate field initialization, for example:
private readonly AddressDetails _details = new AddressDetails
{
EmployeeName = new EmployeeDetails()
};

ListBox does not display information from database

When I click on the ListBox I notice that there are two items.
In my database there are two objects.
When I run my program the ListBox gets the two objects from the database but does not display the name of the two objects.
Here are my codes:
XAML:
<TextBox x:Name="txtSearch" Background="White" GotFocus="txtSearch_Focus" TextChanged="txtSearch_TextChanged" Text="search" FontSize="30" Height="57" Margin="19,10,19,0" Grid.Row="1" />
<!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,72,19,0">
<!--<ScrollViewer>-->
<ListBox Background="Black" x:Name="listBox" FontSize="26" Margin="0,10,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="txtEventName" TextWrapping="Wrap" Foreground="White" Width="300" Margin="10,15,0,0" Height="55"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!--</ScrollViewer>-->
</Grid>
XAML.cs:
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
var eventList = await App.MobileService.GetTable<Event>().ToListAsync();
foreach(Event ename in eventList)
{
eList.Add(ename.EventName);
}
this.listBox.ItemsSource = eList;
this.navigationHelper.OnNavigatedTo(e);
}
private void txtSearch_TextChanged(object sender, TextChangedEventArgs e)
{
if (eList != null)
{
var items = new List<string>();
foreach (var item in eList)
{
if (item.Contains(((TextBox)sender).Text))
items.Add(item);
}
//this.listBox.ItemsSource = items;
}
}
bool hasBeenClicked = false;
private void txtSearch_Focus(object sender, RoutedEventArgs e)
{
if (!hasBeenClicked)
{
txtSearch.Text = String.Empty;
hasBeenClicked = true;
}
}
Event class:
public class Event : IBusinessEntityBase
{
public string Id { get; set; }
public string Image { get; set; }
public string EventName { get; set; }
public string Desc { get; set; } //Description of Event
public string Category { get; set; }
public string Location { get; set; }
public DateTime Date { get; set; } //Should be data type Date
public DateTime StartingTime { get; set; } //Should be of different type (Time?)
//public DateTime EndingTime { get; set; } //Should be of different type (Time?)
//public Boolean PinnedEvent { get; set; }
//public string PinnedEvent { get; set; }
}
in the txtEventName TextBlock you must add Text={Binding EventName}.
EventName or whatever property you want to show in txtEventName.
if eList is a list of strings then your txtEventName must be like this:
<TextBlock x:Name="txtEventName" Text="{Binding}"
TextWrapping="Wrap" Foreground="White" Width="300" Margin="10,15,0,0" Height="55"/>

Populate WPF listbox based on selection of another listbox

I have a listbox that is bound to an observablecollection. The observable collection contains a list of objects, each with it's own observablecollection. What i want is to click an item in the first listbox and have it's list of things displayed in the second listbox. Can I do this in pure WPF?
Just bind the ItemsSource of the second listbox to the SelectedItem of the first list box.
Edit: here is some code.
public partial class MainWindow : Window
{
public MainWindow()
{
TestItems = new ObservableCollection<Test>();
InitializeComponent();
for (int i = 0; i < 5; i++)
TestItems.Add(InitTest(i));
}
public ObservableCollection<Test> TestItems { get; set; }
private Test InitTest(int index)
{
Test test = new Test();
test.Name = "Test" + index.ToString();
test.Test2Items = new ObservableCollection<Test2>();
for (int i = 0; i <= index; i++)
{
Test2 test2 = new Test2();
test2.Label = test.Name + "_label" + i.ToString();
test.Test2Items.Add(test2);
}
return test;
}
}
public class Test
{
public string Name { get; set; }
public ObservableCollection<Test2> Test2Items { get; set; }
public override string ToString()
{
return Name;
}
}
public class Test2
{
public string Label { get; set; }
public override string ToString()
{
return Label;
}
}
Xaml
<Window x:Class="WpfApplication1.MainWindow"
x:Name="MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WPF Example" Height="300" Width="400">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="ListBox1" Grid.Column="0" ItemsSource="{Binding TestItems, ElementName=MyWindow}" />
<ListBox Grid.Column="1" ItemsSource="{Binding SelectedItem.Test2Items, ElementName=ListBox1}" />
</Grid>
</Window>
Your view models could look something like this: (I am using my BindableBase here)
class MainViewModel : Bindablebase {
public ObservableCollection<ItemViewModel> Items { get; private set; }
private ItemViewModel _selectedItem;
public ItemViewModel SelectedItem {
get { return _selectedItem; }
set { SetProperty(ref _selectedItem, value, "SelectedItem"); }
}
}
class ItemViewModel : BindableBase {
public ItemViewModel (string name) {
Name = name;
Items = new ObservableCollection<string>();
}
public string Name { get; private set; }
public ObservableCollection<string> Values { get; private set; }
private string _selectedValue;
public string SelectedValue {
get { return _selectedValue; }
set { SetProperty(ref _selectedValue, value, "SelectedValue"); }
}
}
And then your view would have:
<ComboBox ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}"
DisplayMemberPath="Name"/>
<!--
Note that the DataContext here could be ommitted
and the bindings would be like {Binding SelectedItem.Values}
-->
<ComboBox DataContext="{Binding SelectedItem}"
ItemsSource="{Binding Values}"
SelectedItem="{Binding SelectedValue}"/>

Categories

Resources