i am trying to understand the concept of data binding, i learnt that data binding is the way that you can bind UI elements to objects so that any change in either of them will effect the other(in case of two way binding), but this is not working in this simple example :
my class :
namespace dataBinding.Models
{
public class person
{
public string name { get; set; }
public override string ToString()
{
return this.name;
}
}
}
the XAML :
xmlns:models="using:dataBinding.Models"
<Page.DataContext>
<models:person x:Name="personObject" name="Person's Name"/>
</Page.DataContext>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Right"
Text="{Binding name, Mode=TwoWay}" />
<Button Name="changeNameButton"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Content="Change Name" />
the C# Code :
namespace dataBinding
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
changeNameButton.Click += ChangeNameButton_Click;
}
private void ChangeNameButton_Click(object sender, RoutedEventArgs e)
{
personObject.name = "New Name";
}
}
}
when i run the app the TextBlock shows "Person's Name" but clicking the changeName Button doesn't change what is shown in the TextBlock so, what am i missing here ?
Your missing the INotifyPropertyChanged interface implementation. Your XAML does not know it has been changed.
Easy to fix, use https://github.com/Fody/PropertyChanged It will add the code to notify everyone that the property has changed.
[ImplementPropertyChanged]
public class person
{
public string name { get; set; }
public override string ToString()
{
return this.name;
}
}
Related
I have the below problem: I have two different user controls inside a parent user control. These are trainList, which holds a list of train objects and trainView, which is an user control that shows details of the selected train in the list.
My wish is to share a variable of trainList with trainView.
What I have now is:
Parent user control:
<UserControl>
<UserControl>
<customControls:trainList x:Name="trainList"></customControls:trainList>
</UserControl>
<UserControl>
<customControls:trainView x:Name="trainView"></customControls:trainView>
</UserControl>
<TextBlock DataContext="{Binding ElementName=trainList, Path=SelectedTrain}" Text="{ Binding SelectedTrain.Id }">Test text</TextBlock>
</UserControl>
TrainList class:
public partial class TrainList : UserControl
{
public TrainList()
{
this.InitializeComponent();
this.DataContext = this;
}
public Train SelectedTrain { get; set; }
public void SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Debug.Print(this.SelectedTrain.Id);
}
}
Note: The Train class implements INotifyPropertyChanged.
If I got this to work, I'd apply the binding to the trainView user control (not sure if this would work) instead to the text block.
<UserControl>
<customControls:trainView x:Name="trainView" DataContext="{Binding ElementName=trainList, Path=SelectedTrain}"></customControls:trainView>
</UserControl>
And then, I would access that variable someway from the code-behind of trainView.
(And after this, I would like to share a different variable from trainView with its parent user control, but maybe that's another question).
My current question is: could this be done this way or would I need to follow another strategy?
Take this simple view model, with a base class that implements the INotifyPropertyChanged interface, and a Train, TrainViewModel and MainViewModel class.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void SetValue<T>(
ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (!Equals(storage, value))
{
storage = value;
OnPropertyChanged(propertyName);
}
}
}
public class Train : ViewModelBase
{
private string name;
public string Name
{
get { return name; }
set { SetValue(ref name, value); }
}
private string details;
public string Details
{
get { return details; }
set { SetValue(ref details, value); }
}
// more properties
}
public class TrainViewModel : ViewModelBase
{
public ObservableCollection<Train> Trains { get; }
= new ObservableCollection<Train>();
private Train selectedTrain;
public Train SelectedTrain
{
get { return selectedTrain; }
set { SetValue(ref selectedTrain, value); }
}
}
public class MainViewModel
{
public TrainViewModel TrainViewModel { get; } = new TrainViewModel();
}
which may be initialized in the MainWindow's constructor like this:
public MainWindow()
{
InitializeComponent();
var vm = new MainViewModel();
DataContext = vm;
vm.TrainViewModel.Trains.Add(new Train
{
Name = "Train 1",
Details = "Details of Train 1"
});
vm.TrainViewModel.Trains.Add(new Train
{
Name = "Train 2",
Details = "Details of Train 2"
});
}
The TrainDetails controls would look like this, of course with more elements for more properties of the Train class:
<UserControl ...>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Details}"/>
</StackPanel>
</UserControl>
and the parent UserControl like this, where I directly use a ListBox instead of a TrainList control:
<UserControl ...>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Trains}"
SelectedItem="{Binding SelectedTrain}"
DisplayMemberPath="Name"/>
<local:TrainDetailsControl Grid.Column="1" DataContext="{Binding SelectedTrain}"/>
</Grid>
</UserControl>
It would be instantiated in the MainWindow like this:
<Grid>
<local:TrainControl DataContext="{Binding TrainViewModel}"/>
</Grid>
Note that in this simple example the elements in the UserControls' XAML bind directly to a view model instance that is passed via their DataContext. This means that the UserControl know the view model (or at least their properties). A more general approach is to declare dependency properties in the UserControl class, that are bound to view model properties. The UserControl would then be independent of any particular view model.
Im trying to implement a simple create-function in my win 8 app. Im trying to use the MVVM-pattern. Im trying to pass a class into my view with my view.model and then simply have a couple of textboxes that lets me create a new object. Here is the ViewModel and class:
public class CreateViewModel : ViewModelBase
{
public Place Place { get; set; }
}
public class Place
{
[PrimaryKey, AutoIncrement]
public int PlaceId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
In an MVC-application i would have done some #Html.TextBoxFor and created a post-method.
In XAML I am not sure of how to do this.
The viewmodel gets passed in to view as it should. I can acess its properties like this:
<TextBox Grid.Row="0" Text="{Binding Path=Place.Title}"/>
<TextBox Grid.Row="0" Text="{Binding Path=Place.Description}"/>
But i do not understand how I can "post" new values back to the ViewModel and create a new object?
EDIT:
From what I can see this is a way to have commands in my ViewModel:
public class CreateViewModel : ViewModelBase
{
public RelayCommand CreatePlaceCommand
{
get;
private set;
}
public Place Place { get; set; }
public CreateViewModel()
{
InitializeCommands();
}
private void InitializeCommands()
{
CreatePlaceCommand =
new RelayCommand(() =>
{
//What goes here?
});
}
}
I also added this code to my XAML:
<TextBox Grid.Row="0" Text="{Binding Place.Title,Mode=TwoWay}"/>
<TextBox Grid.Row="0" Text="{Binding Place.Description,Mode=TwoWay}"/>
<Button Grid.Row="0" Content="Click"
Command="{Binding CreatePlaceCommand}" >
</Button>
Am I on the right track here? Its pretty confusing =)
Here, study this simple example to get hold of MVVM/DataBinding/Commands. It's really simplistic but it should show the "patterns" to use. There's plenty of libs (like MVVMLight) to make commanding etc. simpler and more powerful.
So assuming we have Place entity
public class Place
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public override string ToString()
{
return string.Format("Id={0},Title={1},Description={2}",
Id, Title, Description);
}
}
And you have MainWindow.xaml in your application named wpfApplication1
<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"
Height="116"
Width="250">
<!-- set datacontext to mainviewmodel -->
<Window.DataContext>
<wpfApplication1:MainViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<!-- input textboxes for title and description -->
<StackPanel Grid.Row="0">
<TextBox Text="{Binding Place.Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="25" />
<TextBox Text="{Binding Place.Description, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="25" />
</StackPanel>
<!-- button bound to save command, declared in viewmodel -->
<Button Grid.Row="1" Content="Save" Command="{Binding SaveCommand}" />
</Grid>
</Window>
Related MainWindows.xaml.cs contains nothing but InitializeComponents().
Now your MainViewModel, "taking care of all things", could look like
public class MainViewModel
{
private Place _place;
public MainViewModel()
{
// create and register new save command
SaveCommand = new SaveCommand(this);
CommandManager.RegisterClassCommandBinding(
typeof(MainViewModel), new CommandBinding(SaveCommand));
}
// property to hold place data, exposed in UI
public Place Place
{
get { return _place ?? (_place = new Place()); }
set { _place = value; }
}
public ICommand SaveCommand { get; private set; }
}
And simplistic save command implementation used in viewmodel
public class SaveCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private readonly MainViewModel _context;
public SaveCommand(MainViewModel context)
{
_context = context;
}
public void Execute(object parameter)
{
Console.WriteLine(string.Format("Do something with {0}", _context.Place));
}
public bool CanExecute(object parameter)
{
return true;
}
}
Now, this would give you UI, something like below (this example is not type of Store app)
And clicking a button would then spit out
Do something with Id=0,Title=Title,Description=and teh description
I have user control:
<UserControl>
<TextBox Name="TB1"/>
<TextBox Name="TB2"/>
</UserControl>
Code behind:
public partial class MyControl : UserControl
{
public TwoStrings TsObj { get; set; }
public MyControl()
{
InitializeComponent();
}
}
Where
public class TwoStrings
{
string S1 { get; set; }
string S2 { get; set; }
}
How to bind TsObj.S1 to TB1.Text and TsObj.S2 to TB2.Text with minimal code changes? TwoStrings should not be changed. Updates on object should reflect on control. Code example please.
There's a lot of resources about wpf/silverlight databinding on the internet, even here in stackoverflow. But long story short, you can simply do this:
<UserControl>
<TextBox Name="TB1" Text="{Binding S1}" />
<TextBox Name="TB2" Text="{Binding S2}"/>
</UserControl>
public partial class MyControl : UserControl
{
public TwoStrings TsObj { get; set; }
public MyControl()
{
InitializeComponent();
this.DataContext = TsObj = new TwoStrings();
}
}
EDIT: Note that if you change properties via code in the TwoStrings instance, that will be not reflected in the textboxes, because you'll need to implement INotifyPropertyChanged interface in TwoStrings (which is the ViewModel - VM in the MVVM pattern) to get two way databinding. Otherwise, you'll get only one way databinding from your textbox to the properties on the datacontext (TwoString)
i am beginner in mvvm concept.so i tried one sample application.it contains two textbox are name and id, one submit button,one label.when i click submit button it combine the two strings from the textbox and display in label.
i can submit the values and in viewmodel the property contain the result.but its not shown in view.why..?
in view contains
<grid>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding name}" Height="23" Margin="9" Name="txtname" Width="120" />
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding id}" Height="23" Margin="9" Name="txtid" Width="120" />
<Button Command="{Binding submit}" Content="submit" Grid.Column="2" Grid.Row="2" Height="23" Name="btnsubmit" Width="75" />
<Label Content="{Binding display}" Grid.Row="3" Height="38" HorizontalAlignment="Left" Name="lbldisplay" Width="192" />
</grid>
view.cs code is
public MainWindow()
{
InitializeComponent();
DataContext = new DemoViewModel();
}
in my viewmodel contains two .cs file
one is DemoViewModelInotify.cs.in this i write the code for inotifypropertychanged.
another one is DemoViewModel.cs.this contain the property and commands.
namespace mvvmdemonixon.ViewModel
{
public class DemoViewModel :DemoViewModelInotify
{
public ICommand submit { get; set; }
public string name { get; set; }
public string display { get; set; }
public int id{get;set;}
public DemoModel model { get; set; }
public DemoViewModel()
{
submit = new DelegateCommand<object>(this.add);
}
public void add(object paramter)
{
string a= mvvmdemonixon.Model.DemoModel.addstring(name, id);
display = a;
}
}
}
my model contains
namespace mvvmdemonixon.Model
{
public class DemoModel
{
public static string addstring(string name1, int no1)
{
string display = "The Student Name Is " + name1 + "and Id Is" + no1 + ".";
return display;
}
}
}
in my app.xaml.cs
private void OnStartup(object sender, StartupEventArgs e)
{
mvvmdemonixon.MainWindow view = new MainWindow();
view.DataContext = new mvvmdemonixon.ViewModel.DemoViewModel();
view.Show();
}
in my app xaml
<Application x:Class="mvvmdemonixon.App"
Startup="OnStartup">
</Application>
advance thanks..
WPF binding engine uses INotifyPropertyChanged (for scalar properties) and INotifyCollectionChanged (for collection properties) interfaces to reflect changes, which has been made in bound data source (view model).
This:
public string display { get; set; }
means, that any property setting will not notify the view, because setter's code doesn't contain any notifications. The code should be like this:
public string display
{
get { return _display; }
set
{
if (_display != value)
{
_display = value;
OnPropertyChanged("display");
}
}
}
private string _display;
where OnPropertyChanged raises INotifyPropertyChanged.PropertyChanged event.
This is true for other view-bound properties in your view model, which should update the view.
You need to implement INotifyPropertyChanged in your ViewModel.
And raise the property changed event from each property setter.
There are two things you were missing. The first thing is your View's binding is OneTime. The second thing is you ViewModel does not notify when a property changed.
To fix the first problem, you have to update xaml like below.
<Label Content="{Binding display, Mode=OneWay}" Grid.Row="3" Height="38" HorizontalAlignment="Left" Name="lbldisplay" Width="192" />
To fix your second problem, you have to implement INotifyPropertyChanged.
I have a window with a textbox and a submit button. When pressing the submit button, the data in the textbox should populate into the listbox and be saved.
What's the best way of doing this? I tried a recommendation (using ObservableCollection) from an earlier question I had, but I can't seem to get it work. I have tried implementing it like this:
I created a class:
public class AccountCollection
{
private string accountName;
public string AccountName
{
get { return accountName; }
set { accountName = value; }
}
public AccountCollection(string accountName)
{
AccountName = accountName;
}
}
Assigned the binding in my XAML:
<ListBox ItemsSource="{Binding AccountName, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True" Height="164" HorizontalAlignment="Left" Margin="12" Name="accountListBox" VerticalAlignment="Top" Width="161" SelectionChanged="accountListBox_SelectionChanged" />
...and finally, when a user clicks the submit button from another window that contains the Submit button and textbox:
private void okBtn_Click(object sender, RoutedEventArgs e)
{
BindingExpression expression = okBtn.GetBindingExpression(accountaddTextBox.Text);
expression.UpdateSource();
}
But alas, I'm getting nowhere. I get an error message at the GetBindingExpression section:
Argument 1: cannot convert from 'string' to 'System.Windows.DependencyProperty'
What's obvious to me here is that when I created the class I didn't specify anything about the account name from the textbox, so I don't even know if the class is correct.
I'm basically confused and don't know what to do. Any help would be appreciated...
MODEL
// the model is the basic design of an object containing properties
// and methods of that object. This is an account object.
public class Account : INotifyPropertyChanged
{
private string m_AccountName;
public event PropertyChangedEventHandler PropertyChanged;
public string AccountName
{
get { return m_AccountName;}
set
{
m_AccountName = value;
OnPropertyChanged("AccountName");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
ListBox XAML
<ListBox Name="MyAccounts" DisplayMemberPath="AccountName" />
CODE BEHIND
// create a collection of accounts, then whenever the button is clicked,
//create a new account object and add to the collection.
public partial class Window1 : Window
{
private ObservableCollection<Account> AccountList = new ObservableCollection<Account>();
public Window1()
{
InitializeComponent();
AccountList.Add(new Account{ AccountName = "My Account" });
this.MyAccounts.ItemsSource = AccountList;
}
private void okBtn_Click(object sender, RoutedEventArgs e)
{
AccountList.Add(new Account{ AccountName = accountaddTextBox.Text});
}
}
edit: added displaymemberpath on listbox xaml
Here is a Demo using MVVM approach
ViewModel
public class AccountListViewModel : INotifyPropertyChanged
{
ICommand AddAccountCommand {get; set;}
public AccountListViewModel()
{
AccountList = new ObservableCollection<string>();
AddAccountCommand= new RelayCommand(AddAccount);
//Fill account List saved data
FillAccountList();
}
public AddAccount(object obj)
{
AccountList.Add(AccountName);
//Call you Model function To Save you lIst to DB or XML or Where you Like
SaveAccountList()
}
public ObservableCollection<string> AccountList
{
get {return accountList} ;
set
{
accountList= value
OnPropertyChanged("AccountList");
}
}
public string AccountName
{
get {return accountName } ;
set
{
accountName = value
OnPropertyChanged("AccountName");
}
}
}
Xaml Binding
<ListBox ItemsSource="{Binding Path=AccountList}" Height="164" HorizontalAlignment="Left" Margin="12" Name="accountListBox" VerticalAlignment="Top" Width="161" />
<TextBox Text={Binding Path=AccountName}></TextBox>
<Button Command={Binding Path=AddAccountCommand}><Button>
Xaml.cs Code
# region Constructor
/// <summary>
/// Default Constructor
/// </summary>
public MainView()
{
InitializeComponent();
this.DataContext = new AccountListViewModel();
}
# endregion
The Implementation of INotifyPropertyChanged and forming porpeties is left upto you
Your ItemsSource for your ListBox is AccountName, which is only a string but not a collection.
You need to create a viewmodel (your datacontext for the view) like this:
public class ViewModel
{
public ViewModel()
{
Accounts = new ObservableCollection<string>();
}
public ObservableCollection<string> Accounts { get; set; }
}
Bind ItemsSource to Accounts property:
<ListBox ItemsSource="{Binding Accounts}" Height="164" HorizontalAlignment="Left" Margin="12" Name="accountListBox" VerticalAlignment="Top" Width="161" />
And then, in your click event handler of the button you can simple add the current value of the textbox to your collection:
private void okBtn_Click(object sender, RoutedEventArgs e)
{
Accounts.Add(accountaddTextBox.Text);
}
But don't forget to set the DataContext of your window to the class ViewModel.