I'm new in MVVM and I tried to retrieve from 2 textboxes an username and a password. But OnPropertyChanged is not triggered even if I have UpdateSourceTriggered=PropertyChanged. Here is the code. I also tried with an class RelayCommand which uses the delegates Action<> and Func<> but neither that worked.
LogInCommand.cs
using LibraryApp.ViewModels;
using System;
using System.Windows.Input;
namespace LibraryApp.Commands
{
public class LogInCommand : ICommand
{
private LogInViewModel _logInViewModel;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public LogInCommand(LogInViewModel logInViewModel)
{
_logInViewModel = logInViewModel;
}
public bool CanExecute(object parameter) => _logInViewModel.CanUpdate;
public void Execute(object parameter) => _logInViewModel.Execute();
}
}
LogInModel.cs
using System.ComponentModel;
namespace LibraryApp.Models
{
public class LogInModel : INotifyPropertyChanged
{
private string _strUsername;
private string _strPassword;
public string Username
{
get { return _strUsername; }
set {
_strUsername = value;
OnPropertyChanged(Username);
}
}
public string Password
{
get { return _strPassword; }
set
{
_strPassword = value;
OnPropertyChanged(Password);
}
}
public LogInModel(string strUsername, string strPassword)
{
Username = strUsername;
Password = strPassword;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
#endregion
}
}
LogInViewModel.cs
using LibraryApp.Commands;
using LibraryApp.Models;
using LibraryApp.Models.DatabaseModel;
using LibraryApp.Views;
using System.Linq;
using System.Windows;
using System.Windows.Input;
namespace LibraryApp.ViewModels
{
public class LogInViewModel
{
private LogInModel _user;
private LibraryEntities _database;
public LogInModel User
{
get { return _user; }
}
public bool CanUpdate
{
get
{
if (User == null)
{
return false;
}
return !(string.IsNullOrWhiteSpace(User.Username) && string.IsNullOrWhiteSpace(User.Password));
}
}
public ICommand SubmitCommand { get; set; }
public void Execute()
{
if (IsUserInDatabase())
{
UserPage userPage = new UserPage();
userPage.Show();
}
else
{
MessageBox.Show("Username or password are incorrect.");
}
}
public LogInViewModel()
{
_user = new LogInModel("admin", "d7x2rt58");
_database = new LibraryEntities();
SubmitCommand = new LogInCommand(this);
}
public bool IsUserInDatabase()
{
if (_database.BookKeepers.First(it => it.Username == User.Username && it.Password == User.Password) != null)
{
return true;
}
return false;
}
}
}
LogIn.xaml
<Window x:Class="LibraryApp.Views.LogIn"
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:LibraryApp.Views"
mc:Ignorable="d"
Title="LogIn" Height="150" Width="300" WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="1" Content="Log In" HorizontalAlignment="Center"/>
<Label Grid.Row="1" Grid.Column="0" Content="Username"/>
<Label Grid.Row="2" Grid.Column="0" Content="Password"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding LogInModel.Username, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding LogInModel.Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Row="3" Grid.Column="2" Command="{Binding SubmitCommand}" Content="Log In"/>
</Grid>
</Window>
LogIn.xaml.cs
using LibraryApp.ViewModels;
using System.Windows;
namespace LibraryApp.Views
{
/// <summary>
/// Interaction logic for LogIn.xaml
/// </summary>
public partial class LogIn : Window
{
public LogIn()
{
InitializeComponent();
LogInViewModel logInViewModel = new LogInViewModel();
DataContext = logInViewModel;
}
}
}
UserPage.xaml
<Window x:Class="LibraryApp.Views.UserPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:LibraryApp.Views"
mc:Ignorable="d"
Height="450" Width="800"
Title="UserPage">
<Grid>
<Label Content="Welcome" HorizontalAlignment="Center" Margin="0,50,0,0" VerticalAlignment="Top" Height="52" Width="134" FontSize="30"/>
<Button Content="Add book" HorizontalAlignment="Left" Margin="104,145,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Borrow book" HorizontalAlignment="Left" Margin="104,190,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Delete book" HorizontalAlignment="Left" Margin="104,242,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Add user" HorizontalAlignment="Left" Margin="314,145,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Delete user" HorizontalAlignment="Left" Margin="314,195,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Search" HorizontalAlignment="Left" Margin="314,242,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Back" HorizontalAlignment="Left" Margin="648,360,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
</Window>
UserPage.xaml.cs
using System.Windows;
namespace LibraryApp.Views
{
/// <summary>
/// Interaction logic for UserPage.xaml
/// </summary>
public partial class UserPage : Window
{
public UserPage()
{
InitializeComponent();
}
}
}
Welcome to SO. First of all, try to limit the amount of code you post to just the parts that are necessary. People will quickly skip your question when you post a wall of text.
To answer your question...
In LogInModel in the setters of Username and Password you are raising the event by passing in the values of the properties and not the property names themselves:
public string Username
{
get { return _strUsername; }
set
{
_strUsername = value;
OnPropertyChanged(Username);
}
}
Instead you should pass in the name of the property itself:
public string Username
{
get { return _strUsername; }
set
{
_strUsername = value;
OnPropertyChanged(nameof(Username));
// The above is the same as:
// OnPropertyChanged("Username");
}
}
Related
Okay, so I wanna clear the text in my textbox with a click from a button. Both are placed within the first stackpanel in the XAML code. I'm trying to make it work with commands and bindings, but I just can't seem to make it work.. Any suggestions? SqlQueryCommand is bound to the clear button, SqlQueryString is bound to the textbox.
Vievmodel:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using WpfMVVM.Commands;
namespace PoC.ViewModels
{
class MainWindowViewModel
{
public RelayCommand ClearSqlQueryCommand { get; private set; }
public RelayCommand ClearFilterCommand { get; private set; }
public RelayCommand ClearFilterByIdCommand { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
private string sqlQueryString;
private string filterString;
private string filterByIdString;
public string SqlQueryString
{
get
{
return sqlQueryString;
}
set
{
sqlQueryString = value;
OnPropertyChanged(nameof(SqlQueryString));
}
}
public string FilterString
{
get
{
return filterString;
}
set
{
filterString = value;
OnPropertyChanged(nameof(FilterString));
}
}
public string FilterByIdString
{
get
{
return filterByIdString;
}
set
{
filterByIdString = value;
OnPropertyChanged(nameof(FilterByIdString));
}
}
public MainWindowViewModel()
{
ClearSqlQueryCommand = new RelayCommand(ClearSqlQuery, CanClearSqlQuery);
ClearFilterCommand = new RelayCommand(ClearFilter, CanClearFilter);
ClearFilterByIdCommand = new RelayCommand(ClearFilterById, CanClearFilterById);
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public void ClearSqlQuery(object parameter)
{
SqlQueryString = string.Empty;
}
public bool CanClearSqlQuery(object parameter)
{
return true;
}
public void ClearFilter(object parameter)
{
}
public bool CanClearFilter(object parameter)
{
return true;
}
public void ClearFilterById(object parameter)
{
}
public bool CanClearFilterById(object parameter)
{
return true;
}
}
}
RelayCommand:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;
namespace WpfMVVM.Commands
{
public class RelayCommand : ICommand
{
Action<object> _execute;
Func<object, bool> _canExecute;
public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
{
return _canExecute(parameter);
}
else
{
return false;
}
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
}
XAML:
<Window x:Class="PoC.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:PoC"
xmlns:viewModels="clr-namespace:PoC.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<viewModels:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<TreeView>
</TreeView>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="1*"/>
<RowDefinition Height="5*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" BorderThickness="0 0 0 1" BorderBrush="Black"/>
<StackPanel Grid.Row="0" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBox Text="{Binding SqlQueryString, UpdateSourceTrigger=PropertyChanged}" Height="30" Width="450" TextAlignment="Center"/>
<Button Content="Execute" Margin="10 0" Padding="5 0"/>
<Button Content="Clear" Padding="5 0" Command="{Binding ClearSqlQueryCommand}"/>
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBox Text="{Binding FilterString}" Grid.Row="0" Height="30" Width="200"/>
<Button Content="Filter" Margin="10 0" Padding="5 0"/>
<Button Content="Clear" Padding="5 0" Margin="0 0 115 0"/>
<TextBox Text="{Binding FilterByIdString}" Grid.Row="0" Height="30" Width="100"/>
<Button Content="Filter" Margin="10 0 0 0" Padding="5 0"/>
</StackPanel>
<DataGrid Grid.Row="2" VerticalScrollBarVisibility="Visible" BorderThickness="0 1 1 1"/>
</Grid>
</Grid>
You seem to be missing : INotifyPropertyChangedon the MainWindowViewModelclass.
You can easily do that by setting its value to "".
texbox.Text="";
I'm working on a bank application in c# wpf using the MVVM pattern which allows a manager in charge of a branch to display the data of one of the customers of this branch in another tab by selecting the line corresponding to this customer. I use a custom framework.
Here is my Xaml code corresponding to the main view that displays the agencies for which the Manager is responsible:
<f:UserControlBase x:Class="BankingApp.View.ManagerAgencyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:BankingApp.ViewModel"
xmlns:vw="clr-namespace:BankingApp.View"
xmlns:f="clr-namespace:PRBD_Framework;assembly=PRBD_Framework"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:ManagerAgencyViewModel,
IsDesignTimeCreatable=False}"
FontSize="14" d:DesignHeight="498" d:DesignWidth="918">
<UserControl.DataContext>
<vm:ManagerAgencyViewModel x:Name="vm"/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Margin="5" Text="Agency:"/>
<ComboBox
Margin="5,5,5,5"
Grid.Column="1"
ItemsSource="{Binding Path=Agencies,Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedAgency}"
/>
<f:MyDataGrid
Grid.Row="1"
ItemsSource="{Binding Clients}"
SelectedItem="{Binding SelectedClient}"
CanUserAddRows="False"
AutoGenerateColumns="False"
x:Name="gridAccesses" Grid.ColumnSpan="2" Margin="0,0,5,0"
>
<DataGrid.Columns>
<DataGridTextColumn Header="idClient" Width="auto" Binding="{Binding UserId}"
IsReadOnly="True" />
<DataGridTextColumn Header="FirstName" Width="*" Binding="{Binding FirstName}"
IsReadOnly="True" />
<DataGridTextColumn Header="LastName" Width="*" Binding="{Binding LastName}"
IsReadOnly="True" />
</DataGrid.Columns>
</f:MyDataGrid>
<Button Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" Width="80" Margin="10"
Content="New Client" Command=""/>
<f:MyTabControl Grid.Row="3" Grid.ColumnSpan="2" >
<TabItem Header="Client">
<vw:ManagerClientDataView x:Name="ClientData" DataContext="{Binding
ManagerClientData, ElementName=vm}"/>
</TabItem>
<TabItem Header="Account">
<vw:ManagerClientAccount/>
</TabItem>
</f:MyTabControl>
</Grid>
</f:UserControlBase>
When I select on one of the agencies, it shows me all the clients of this agency like this:
enter image description here
here is the corresponding viewModel
using BankingApp.Model;
using PRBD_Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BankingApp.ViewModel {
public class ManagerAgencyViewModel : ViewModelCommon {
public ManagerClientDataViewModel ManagerClientData { get; private set; } = new
ManagerClientDataViewModel();
public ManagerAgencyViewModel() {
OnRefreshData();
}
public ObservableCollectionFast<Agency> Agencies { get; set; } = new();
public ObservableCollectionFast<Client> Clients { get; set; } = new();
private Client _selectedClient;
public Client SelectedClient {
get => _selectedClient;
set {
SetProperty(ref _selectedClient, value, () => ManagerClientData.SelectedClient =
value);
}
}
private Agency _selectedAgency;
public Agency SelectedAgency {
get => _selectedAgency;
set => SetProperty(ref _selectedAgency, value, () => ClientOfAgency(SelectedAgency));
}
private void ClientOfAgency(Agency agency) {
if (agency != null)
Clients.RefreshFromModel(Context.Clients.Where(c => c.Agency.AgencyId ==
agency.AgencyId));
}
protected override void OnRefreshData() {
Agencies.RefreshFromModel(Context.Agencies.OrderBy(a => a.Name));
ClientOfAgency(SelectedAgency);
}
public void init() {
}
}
}
Here is the code of my tab to display the data of the selected client:
<f:UserControlBase x:Class="BankingApp.View.ManagerClientDataView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:BankingApp.ViewModel"
xmlns:vw="clr-namespace:BankingApp.View"
xmlns:f="clr-namespace:PRBD_Framework;assembly=PRBD_Framework"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Margin="0,10,0,10">
<TextBlock Text="FirstName:"/>
<TextBox Text="{Binding Firstname}"/>
</StackPanel>
<StackPanel Grid.Row="1" Margin="0,0,0,10">
<TextBlock Text="LastName:"/>
<TextBox Text="{Binding Lastname}"/>
</StackPanel>
<StackPanel Grid.Row="2" Margin="0,0,0,10">
<TextBlock Text="Email:"/>
<TextBox Text="{Binding Email}"/>
</StackPanel>
<StackPanel Grid.Row="3" Margin="0,0,0,10">
<TextBlock Text="Password:"/>
<PasswordBox f:PasswordHelper.Password="{Binding Password, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<StackPanel Grid.Row="4">
<TextBlock Text="Confirm Password:"/>
<PasswordBox f:PasswordHelper.Password="{Binding Password, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
</f:UserControlBase>
And the corresponding viewmodel
using BankingApp.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PRBD_Framework;
namespace BankingApp.ViewModel {
public class ManagerClientDataViewModel : ViewModelCommon {
private Client _selectedClient;
public Client SelectedClient {
get => _selectedClient;
set {
SetProperty(ref _selectedClient, value);
Console.WriteLine(SelectedClient.FirstName);
}
}
public string Firstname {
get => SelectedClient?.FirstName;
set => SetProperty(SelectedClient.FirstName, value, SelectedClient, (s, v) => {
s.FirstName = v;
Console.WriteLine(s.FirstName);
});
}
public string Lastname {
get => SelectedClient?.LastName;
set => SetProperty(SelectedClient.LastName, value, SelectedClient, (s, v) => {
s.LastName = v;
Console.WriteLine(s.LastName);
});
}
public string Email {
get => SelectedClient?.Email;
set => SetProperty(SelectedClient.Email, value, SelectedClient, (s, v) => {
s.Email = v;
Console.WriteLine(s.Email);
});
}
public string Password {
get => SelectedClient?.Password;
set => SetProperty(SelectedClient.Password, value, SelectedClient, (s, v) => {
s.Password = v;
});
}
}
}
My problem is that when I select a customer, his data does not appear in the form, could someone help me? Thanks
this is a working example without the use of any frameworks. Maybe this helps to track down your issue or just use this code ;-)
XAML:
<Window x:Class="WpfApp1AgencyTest.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:WpfApp1AgencyTest"
mc:Ignorable="d"
xmlns:vm="clr-namespace:WpfApp1AgencyTest"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:ManagerAgencyViewModel/>
</Window.DataContext>
<Grid>
<TabControl>
<TabItem Header="Manager" >
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Margin="5" Text="Agency:"></TextBlock>
<ComboBox Grid.Column="1" Margin="5,5,5,5"
ItemsSource="{Binding Path=Agencies}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedAgency}"
></ComboBox>
</Grid>
<DataGrid
Grid.Row="1"
ItemsSource="{Binding SelectedAgency.Clients}"
SelectedItem="{Binding SelectedClient}"
AutoGenerateColumns="False"
>
<DataGrid.Columns>
<DataGridTextColumn Header="id" Binding="{Binding ID}"></DataGridTextColumn>
<DataGridTextColumn Header="First name" Binding="{Binding Firstname}"></DataGridTextColumn>
<DataGridTextColumn Header="Last name" Binding="{Binding Lastname}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<TabControl>
<TabItem Header="Client">
<StackPanel DataContext="{Binding SelectedClient}">
<TextBlock>First name:</TextBlock>
<TextBox Text="{Binding Firstname}"></TextBox>
<TextBlock>Last name:</TextBlock>
<TextBox Text="{Binding Lastname}"></TextBox>
<TextBlock>Password:</TextBlock>
<TextBox Text="{Binding Password}"></TextBox>
<TextBlock>Confirm Password:</TextBlock>
<TextBox></TextBox>
</StackPanel>
</TabItem>
<TabItem Header="Account">
</TabItem>
</TabControl>
</StackPanel>
</TabItem>
</TabControl>
</Grid>
</Window>
class Client
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApp1AgencyTest
{
public class Client : INotifyPropertyChanged
{
private string id;
public string ID
{
get { return id; }
set { id = value; RaisePropertyChange(); }
}
private string firstname;
public string Firstname
{
get => firstname; set
{
firstname = value;
RaisePropertyChange();
}
}
private string lastname;
public string Lastname
{
get => lastname; set
{
lastname = value;
RaisePropertyChange();
}
}
private string password;
public string Password
{
get => password; set
{
password = value;
RaisePropertyChange();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChange([CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
}
}
class Agency
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApp1AgencyTest
{
public class Agency
{
private string name;
public string Name
{
get { return name; }
set { name = value; RaisePropertyChange(); }
}
public ObservableCollection<Client> Clients { get; set; } = new();
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChange([CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
}
}
class ManagerAgencyViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApp1AgencyTest
{
public class ManagerAgencyViewModel
{
public ManagerAgencyViewModel()
{
Agencies =
new ObservableCollection<Agency>() {
new Agency()
{
Name="Dexia",
Clients = new ObservableCollection<Client>()
{
new Client(){ID="1", Firstname = "b",Lastname="inconnu"},
new Client(){ ID="2",Firstname = "Mustafa",Lastname="Azoud" },
new Client{ID="3",Firstname="Samiha",Lastname="Draa"}
}
},
new Agency()
{
Name="2Advacend",
Clients = new ObservableCollection<Client>()
{
new Client(){ ID="4", Firstname = "Joe",Lastname="Doe"},
new Client(){ ID="5", Firstname = "Max",Lastname="Headroom" },
}
}
};
}
public ObservableCollection<Agency> Agencies { get; set; }
private Agency selectedAgency;
public Agency SelectedAgency
{
get => selectedAgency;
set
{
selectedAgency = value;
RaisePropertyChange();
}
}
private Client selectedClient;
public Client SelectedClient { get => selectedClient; set {
selectedClient = value;
RaisePropertyChange();
} }
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChange([CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
}
}
I have the following view here where the user is able to input three different items: LockedOutBy, LockedOutFor, and LockedOutDate. I am having trouble creating a method within my view-model to allow the user to input these three items and once they hit 'ok', it will save the three items. Sorry if it is vague or if there is not enough info, please let me know if there is anything else needed.
Thank you.
View
<TextBlock VerticalAlignment="Center" Margin="5,5" Grid.Column="0" Grid.Row="1" Text="Locked Out By:"/>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding LockedOutBy, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock VerticalAlignment="Center" Margin="5,5" Grid.Column="0" Grid.Row="2" Text="Locked Out For:"/>
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding LockedOutFor, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock VerticalAlignment="Center" Margin="5,5" Grid.Column="0" Grid.Row="3" Text="Locked Out Date:"/>
<TextBox Grid.Column="1" Grid.Row="3" Text="{Binding LockedOutDate, UpdateSourceTrigger=PropertyChanged}"/>
<Button Command="{Binding Path=OKCommand}"
View-Model
This is what I have so far in my view-model.
private string _LockedOutFor;
public string LockedOutFor
{
get { return _LockedOutFor; }
set
{
_LockedOutFor = value;
OnPropertyChanged("OwnerName");
}
}
private string _LockedOutBy;
public string LockedOutBy
{
get { return _LockedOutBy; }
set
{
_LockedOutBy = value;
OnPropertyChanged("Street");
}
}
private int _LockedOutDate;
public int LockedOutDate
{
get { return _LockedOutDate; }
set
{
_LockedOutDate = value;
OnPropertyChanged("StreetOverflow");
}
}
public ICommand CancelCommand
{
get { return new RelayCommand(c => OnCancelLock()); }
}
public ICommand OKCommand
{
get { return new RelayCommand(c => OnOKLock()); }
}
protected void OnOKLock()
{
OnOK(LockedOutFor, LockedOutBy, LockedOutDate);
}
protected void OnCancelLock()
{
OnCancel();
}
ANotifyPropertyChanged - base implementation for all of our view models
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace SO60269403
{
public abstract class ANotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void SetProperty<T>(ref T backingStore, T newValue, [CallerMemberName] string propertyName = "")
{
var areEqual = ReferenceEquals(backingStore, newValue);
if (areEqual)
return;
backingStore = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
LockedOutViewModel - our view model
namespace SO60269403
{
public class LockedOutViewModel : ANotifyPropertyChanged
{
private string lockedOutBy;
private string lockedOutFor;
private int lockedOutDate;
public string LockedOutBy { get => lockedOutBy; set => SetProperty(ref lockedOutBy, value); }
public string LockedOutFor { get => lockedOutFor; set => SetProperty(ref lockedOutFor, value); }
public int LockedOutDate { get => lockedOutDate; set => SetProperty(ref lockedOutDate, value); }
}
}
ACommand - base implementation for all of our commands
using System;
using System.Windows.Input;
namespace SO60269403
{
public abstract class ACommand : ICommand
{
public event EventHandler CanExecuteChanged;
public virtual bool CanExecute(object parameter) => true;
public abstract void Execute(object parameter);
}
}
SaveCommand - our save command
namespace SO60269403
{
public class SaveCommand : ACommand
{
public LockedOutViewModel LockedOutViewModel { get; set; }
public override void Execute(object parameter)
{
//TODO
}
}
}
MainWindow - a view to data-bind our view model and hook up our save command
<Window
x:Class="SO60269403.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SO60269403">
<Window.Resources>
<local:LockedOutViewModel
x:Key="LockedOutViewModel" />
</Window.Resources>
<Grid
DataContext="{StaticResource LockedOutViewModel}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0">Locked Out By</TextBlock>
<TextBlock
Grid.Row="1"
Grid.Column="0">Locked Out For</TextBlock>
<TextBlock
Grid.Row="2"
Grid.Column="0">Locked Out Date</TextBlock>
<TextBox
Grid.Row="0"
Grid.Column="1"
Text="{Binding LockedOutBy}" />
<TextBox
Grid.Row="1"
Grid.Column="1"
Text="{Binding LockedOutFor}" />
<TextBox
Grid.Row="2"
Grid.Column="1"
Text="{Binding LockedOutDate}" />
<StackPanel
Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"
Orientation="Horizontal"
HorizontalAlignment="Right">
<StackPanel.Resources>
<Style
TargetType="Button">
<Setter
Property="Width"
Value="100" />
</Style>
</StackPanel.Resources>
<Button>Cancel</Button>
<Button>
<Button.Command>
<local:SaveCommand
LockedOutViewModel="{StaticResource LockedOutViewModel}" />
</Button.Command>
OK
</Button>
</StackPanel>
</Grid>
</Window>
if we put a breakpoint in SaveCommand.Execute then run the app, fill in some values and click the OK button, the debugger should break on our breakpoint and we can observe that the view model's properties match what we entered. this demonstrates that the data is in place ready to be saved.
I have 3 text boxes and when user enter value in them and press save button then they should add the data they contain to the data grid.
Every thing works fine and binding to the textboxes and button is done well but i do not understand how to update datagrid using the value user entered in textboxes.
My full code is here :
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding TextName}" Height="20" Width="80" HorizontalAlignment="Center"></TextBox>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding RollNumber}" Height="20" Width="80"></TextBox>
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding Class}" Height="20" Width="80"></TextBox>
<Label Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center">Name</Label>
<Label Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">RollNumber</Label>
<Label Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center">Class</Label>
</Grid>
<Grid Grid.Row="1" >
<Button Width="80" Height="20" Command="{Binding SaveStudentRecord}"> Save</Button>
</Grid>
<Grid Grid.Row="2">
<DataGrid ItemsSource="{Binding DGrid}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding DgName}" Width="150"></DataGridTextColumn>
<DataGridTextColumn Header="Rollnumber" Binding="{Binding dgRollnumber}" Width="150"></DataGridTextColumn>
<DataGridTextColumn Header="Class" Binding="{Binding dgClass}" Width="150"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Grid>
</Window>
ViewModel is:
class ViewModel
{
private string textName;
private string rollNumber;
private string cclass;
private RelayCommand saveStudentRecord;
private Model editModel;
public string TextName
{
get { return textName; }
set
{
textName = value;
PropertyChangedEventArgs("TextName");
}
}
public string RollNumber
{
get { return rollNumber; }
set
{
rollNumber = value;
PropertyChangedEventArgs("RollNumber");
}
}
public string Class
{
get { return cclass; }
set
{
rollNumber = value;
PropertyChangedEventArgs("Class");
}
}
public bool canExecute { get; set; }
public Model EditModel
{
get
{
return editModel ;
}
set
{
editModel = value;
PropertyChangedEventArgs("EditModel");
}
}
public ViewModel()
{
canExecute = true;
}
public RelayCommand SaveStudentRecord
{
get { return saveStudentRecord = new RelayCommand(() => MyAction(), canExecute); }
}
private void MyAction()
{
string chck1 = TextName; //I see on debugging that TextName contains the text entered so how to add this text to Datagrid column
string chck2 = Class;
string chck3 = RollNumber;
// How to add this data to datagrid
MessageBox.Show("Hii");
}
public event PropertyChangedEventHandler PropertyChanged;
private void PropertyChangedEventArgs(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
What exactly i mean is how to bind the datagrid inside the MyAction() such that all the three textbox strings will be added to the respective columns ?
As others have suggested in comments I'm not sure what Dgrid represents, but I think a simple example should help:
This is just a window with a DataGrid, TextBox and a button. When you type something in the datagrid and press the button it adds the value to the datagrid. It's done MVVM, I hope this demonstrates the process.
(.NET 4.6 syntax. If it doesn't work change the observable collection and move the creation of it to the constructor)
MainView.xaml
<Window x:Class="WpfApplication4.MainView"
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"
mc:Ignorable="d"
Title="MainView" Height="350" Width="525">
<Grid>
<DataGrid HorizontalAlignment="Left" Height="207" Margin="103,46,0,0" VerticalAlignment="Top" Width="311" ItemsSource="{Binding Stuff}" AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding}"/>
</DataGrid.Columns>
</DataGrid>
<TextBox HorizontalAlignment="Left" Height="20" Margin="167,9,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="179" Text="{Binding TextValue, Mode=TwoWay}"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="351,8,0,0" VerticalAlignment="Top" Width="75" Command="{Binding GoCommand}"/>
</Grid>
</Window>
MainViewModel.cs
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace WpfApplication4
{
public class MainViewModel
{
public ObservableCollection<string> Stuff { get; set; } = new ObservableCollection<string>();
public ICommand GoCommand { get; set; }
public string TextValue { get; set; }
public MainViewModel()
{
Stuff.Add("a");
Stuff.Add("b");
Stuff.Add("c");
Stuff.Add("d");
GoCommand = new RelayCommand((p) => Stuff.Add(TextValue));
}
}
}
MainView.xaml.cs
using System.Windows;
namespace WpfApplication4
{
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
}
RelayCommand.cs
using System;
using System.Diagnostics;
using System.Windows.Input;
namespace WpfApplication4
{
public class RelayCommand : ICommand
{
private readonly Action<object> execute;
private readonly Predicate<object> canExecute;
public RelayCommand(Action<object> execute) : this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null) throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return canExecute == null || canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
execute(parameter);
}
}
}
As the title says, im trying to display the hardcoded values I typed in my XAML file through databindings but in 1 method call instead of binding the textboxes in every specific property.
Model:
public class Person
{
public string Namn { get; set; }
public DateTime Födelsedatum { get; set; }
public int Betyg { get; set; }
public int AntalBarn { get; set; }
public int Favoritsiffra { get; set; }
public string Kommentar { get; set; }
}
View:
<Window x:Class="PiedPiper.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Inlämningsuppgift WPF" Height="400" Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Content="Personuppgifter" FontSize="35" Grid.Column="0" Grid.Row="0"></Label>
<Separator Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="10,2,-50,2"/>
<Label Content="Namn:" Grid.Column="0" Grid.Row="2" Margin="5,5,210,0"></Label>
<TextBox Name="NamnTextBox" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="118,5,-50,0"/>
<Label Content="Födelsedatum:" Grid.Column="0" Grid.Row="3" Margin="5,5,210,0"></Label>
<TextBox Name="FödelsedatumTextBox" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Margin="118,5,-50,0"></TextBox>
<Label Content="Betyg" Grid.Column="0" Grid.Row="4" Margin="5,5,210,0"></Label>
<ComboBox Name="BetygComboBox" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Margin="218,5,-50,0"></ComboBox>
<Label Content="Antal barn (0-20):" Grid.Column="0" Grid.Row="5" Margin="5,5,210,0"></Label>
<Slider Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Margin="125,5,25,0"></Slider>
<TextBox Name="AntalBarnTextBox" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Margin="300,5,-50,0"></TextBox>
<Label Content="Favoritsiffra (0-99):" Grid.Column="0" Grid.Row="6" Margin="5,5,210,0"></Label>
<TextBox Name="FavoritsiffraTextBox" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Margin="225,5,-50,0"></TextBox>
<CheckBox Content="Visa kommentar" Grid.Row="7" Grid.Column="0" Margin="5,5,210,0"></CheckBox>
<TextBox Name="KommentarTextBox" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" Margin="125,5,-50,-50" Grid.RowSpan="2"></TextBox>
<Button Content="Spara" Grid.Row="8" Grid.Column="0" Margin="125,55,-50,-80" Grid.ColumnSpan="2"></Button>
<Separator Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" Margin="10,85,-50,-80"></Separator>
<Label Content="Andre Kordasti" Grid.Row="10" Grid.Column="0" Margin="10,85,-50,-80"></Label>
<Button Content="Avsluta" Grid.Row="10" Grid.Column="0" Margin="225,85,-50,-80" Grid.ColumnSpan="2"></Button>
</Grid>
ViewModel:
public class PersonViewModel : INotifyPropertyChanged
{
public PersonViewModel(Person person)
{
Namn = person.Namn;
Födelsedatum = person.Födelsedatum;
Betyg = person.Betyg;
AntalBarn = person.AntalBarn;
Favoritsiffra = person.Favoritsiffra;
Kommentar = person.Kommentar;
GetPerson();
}
private void GetPerson()
{
Namn = "KurtSune";
Födelsedatum = new DateTime(1980, 09, 06);
Betyg = 3;
AntalBarn = 7;
Favoritsiffra = 10;
Kommentar = "Kommentar...";
var mainWindow = new MainWindow();
mainWindow.NamnTextBox.Text = Namn;
mainWindow.FödelsedatumTextBox.Text = Convert.ToString(Födelsedatum);
mainWindow.BetygComboBox.SelectedValue = Betyg;
mainWindow.AntalBarnTextBox.Text = Convert.ToString(AntalBarn);
mainWindow.FavoritsiffraTextBox.Text = Convert.ToString(Favoritsiffra);
mainWindow.KommentarTextBox.Text = Kommentar;
}
public ICommand SaveCommand { get; set; }
public ICommand AbortCommand { get; set; }
public bool CanSave
{
get
{
if (Namn == null)
{
return false;
}
return !String.IsNullOrWhiteSpace(Namn);
}
}
private string _namn;
public string Namn
{
get { return _namn; }
set { _namn = value; OnPropertyChanged("Namn"); }
}
private DateTime _födelsedatum;
public DateTime Födelsedatum
{
get { return _födelsedatum; }
set { _födelsedatum = value; OnPropertyChanged("Födelsedatum"); }
}
private int _betyg;
public int Betyg
{
get { return _betyg; }
set { _betyg = value; OnPropertyChanged("Betyg"); }
}
private int _antalBarn;
public int AntalBarn
{
get { return _antalBarn; }
set { _antalBarn = value; OnPropertyChanged("AntalBarn"); }
}
private int _favoritSiffra;
public int Favoritsiffra
{
get { return _favoritSiffra; }
set { _favoritSiffra = value; OnPropertyChanged("Födelsedatum"); }
}
private string _kommentar;
public string Kommentar
{
get { return _kommentar; }
set { _kommentar = value; OnPropertyChanged("Kommentar"); }
}
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
And to add my App.xaml.cs file:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var person = new Person();
var personViewModel = new PersonViewModel(person);
var mainWindow = new MainWindow();
mainWindow.DataContext = personViewModel;
mainWindow.Show();
}
}
So I dont get the following values to be displayed... Please help, Iam new to this.
Here is a simplified example where,
I made a DataTemplate where controls are bound to the Person properties. Benefits are obvious because as you will see whichever end is modified, the other will know/show these changes.
(added a button and a MessageBox to further demonstrate this)
Code:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
var person = new Person {Name = "name1", Address = "address1"};
person.PropertyChanged += person_PropertyChanged;
DataContext = person;
}
private void person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
MessageBox.Show("data changed");
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var dataContext = (Person) DataContext;
dataContext.Name = "newName";
dataContext.Address = "newAddress";
}
}
internal class Person : INotifyPropertyChanged
{
private string _address;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public string Address
{
get { return _address; }
set
{
_address = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow"
Width="525"
Height="350">
<Window.Resources>
<DataTemplate x:Key="PersonTemplate" DataType="wpfApplication1:Person">
<StackPanel>
<TextBlock Text="Name" />
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Address" />
<TextBox Text="{Binding Address, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<Button Click="ButtonBase_OnClick" Content="Modify" />
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource PersonTemplate}" />
</StackPanel>
</Grid>
</Window>
Now as you can see there are no hard-coded values in XAML, the object I have passed to DataContext has sample values instead.