I have a Xaml Page With A textbox And a button When the User Clicks the Button The text box value should be passed to its viewModel. How to achieve this?
xaml:
<TextBox Text={Binding TextBoxAContent} />
<Button Command={Binding ButtonCommand} />
view model code should be something like this:
class MainPageViewModel : ViewModelBase
{
private string _textBoxAContent;
public string TextBoxAContent
{
get {return _textBoxAContent;}
set {
_textBoxAContent = value;
RaisePropertyChanged("TextBoxAContent");
}
}
public ICommand ButtonCommand
{
get
{
return new RelayCommand(ProcessTextHandler);
}
}
private void ProcessTextHandler()
{
//add your code here. You can process your textbox`s text using TextBoxAContent property.
}
}
You should also assign your viewmodel to view via DataContext property of the view control. (simply in constructor)
public MainPage()
{
DataContext = new MainPageViewModel();
}
UPD
p.s. RelayCommand & ViewModelBase - classes from MVVM Light
Related
Here I have added a boolean property in model and binding it to Isenabled property of Combobox . And Binding Command to button in which I have added execute() method which sets the property binded to combobox. However this changes isEnable of Button. Is there a way to disable combobox on btn click?
Model:
public class MyPanelModel : BindableBase
{
private bool _MessageVisibilty;
public bool MessageVisibilty
{
get
{
return _MessageVisibilty;
}
set
{
SetProperty(ref _MessageVisibilty, value);
}
}
}
View Model:
private MyPanelModel plModel;
public MyPanelModel MyPnlModel
{
get { return plModel; }
set { SetProperty(ref plModel,value); }
}
private readonly DelegateCommand _hideCommand;
public DelegateCommand DisableOtherControlsCommand
{
get { return _hideCommand; }
}
public MyPanelViewModel()
{
_hideCommand = new DelegateCommand(hideExecute, canExecute);
_hideCommand.RaiseCanExecuteChanged();
}
private void hideExecute()
{
MyPnlModel.MessageVisibilty = false;
}
private bool canExecute()
{
return true;
}
Xaml :
<ComboBox Mandatory="True">
<i:Interaction.Triggers >
<i:EventTrigger EventName="IsEnabled">
<prism:InvokeCommandAction Command="{Binding MyPanelModel.MessageVisibilty,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
<Button Margin="10" Command="{Binding MyPanelViewModel.DisableOtherControlsCommand}" >Save</Button>
If you want to use MVVM the idea is to bind the IsEnable property of your button to a boolean in your viewmodel.
If your viewmodel implements INotifyPropertyChanged and is set as a datacontext for your view, when you will update the boolean value in your viewmodel, the control which is bind to the boolean property should be enable/disabled
If you wand to change the boolean after a click on the button you can simply create a Command and bind your button Command property on it. In your command method implementation, set the boolean value to true/false it will reflect on the ui's isenable property immediately
For Command creation, here is another topic that may help you :
How to bind WPF button to a command in ViewModelBase?
I have a custom class which a usercontrol has implemented as a dependency property in it's code behind.
public partial class HandControl
{
public HandControl()
{
InitializeComponent();
}
public Seat Seat
{
get
{
return (Seat)GetValue(SeatProperty);
}
set
{
SetValue(SeatProperty, value);
}
}
public static readonly DependencyProperty SeatProperty = DependencyProperty.Register("Seat", typeof(Seat), typeof(HandControl), new PropertyMetadata(null));
}
In my case I've bound the name property in that class to a label inside the usercontrols xaml.
<Label Content="{Binding Seat.Player.Name, RelativeSource={RelativeSource AncestorType={x:Type controls:HandControl}}}"/>
The view model of my window contains the property SeatTl and the xaml is binding to it:
public Seat SeatTr
{
get { return _seatTr; }
private set
{
_seatTr = value;
OnPropertyChanged();
}
}
<customControls:HandControl Grid.Row="1"
Grid.Column="3"
Seat="{Binding SeatTr}" />
However, when I change my class content (the name property) and manually raise OnPropertyChanged in my viewmodel (not the usercontrol), the label is not updated and still has the same content.
private void OnSeatChanged(Player player, SeatPosition seatPosition)
{
//... doing the changes ...\\
OnPropertyChanged("SeatTr");
}
Whats my problem? Anyone got a clue?
I think u should raise OnPropertyChanged for Seat.Player.Name property as It is being chaged.
I'm developing a WPF application and I'm struggling a little bit to understand some of the details of DataContext as it applies to binding. My application uses a business object which is defined like this:
public class MyBusinessObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
// enumerations for some properties
public enum MyEnumValues
{
[Description("New York")]
NewYork,
[Description("Chicago")]
Chicago,
[Description("Los Angeles")]
LosAngeles
}
// an example property
private string _myPropertyName;
public string MyPropertyName
{
get { return _myPropertyName; }
set
{
if (_myPropertyName == value)
{
return;
}
_myPropertyName = value;
OnPropertyChanged(new PropertyChangedEventArgs("MyPropertyName"));
}
}
// another example property
private MyEnumValues _myEnumPropertyName;
public MyEnumValues MyEnumPropertyName
{
get { return _myEnumPropertyName; }
set
{
if (_myEnumPropertyName== value)
{
return;
}
_myEnumPropertyName= value;
OnPropertyChanged(new PropertyChangedEventArgs("MyEnumPropertyName"));
}
}
// example list property of type Widget
public List<Widget> MyWidgets { get; set; }
// constructor
public MyBusinessObject()
{
// initialize list of widgets
MyWidgets = new List<Widget>();
// add 10 widgets to the list
for (int i = 1; i <= 10; i++)
{
MyWidgets.Add(new Widget());
}
// set default settings
this.MyPropertyName = string.empty;
}
}
As you can see, I have some properties that are declared in this class one of which is a list of Widgets. The Widget class itself also implements INotifyPropertyChanged and exposes about 30 properties.
My UI has a combobox which is bound to my list of Widgets like this:
MyBusinessObject myBusinessObject = new MyBusinessObject();
public MainWindow()
{
InitializeComponent();
this.DataContext = myBusinessObject;
selectedWidgetComboBox.ItemsSource = myBusinessObject.MyWidgets;
selectedWidgetComboBox.DisplayMemberPath = "WidgetName";
selectedWidgetComboBox.SelectedValuePath = "WidgetName";
}
The majority of the controls on my UI are used to display the properties of a Widget. When my user selects a Widget from the combobox, I want these controls to display the properties for the selected Widget. I'm currently achieving this behavior by updating my window's DataContext in the SelectionChanged event handler of my combobox like this:
private void selectedWidgetComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.DataContext = selectedWidgetComboBox.SelectedItem;
}
This allows me to bind my controls to the appropriate Widget property like this:
<TextBox Text="{Binding WidgetColor}"></TextBox>
However, not all of the controls in my UI are used to display Widget properties. Some of the controls need to display the properties from MyBusinessObject (for example: MyPropertyName defined above). In this scenario, I can't simply say:
<TextBox Text="{Binding MyPropertyName}"></TextBox>
...because the DataContext of the window is pointing to the selected Widget instead of MyBusinessObject. Can anyone tell me how I set the DataContext for a specific control (in XAML) to reference the fact that MyPropertyName is a property of MyBusinessObject? Thank you!
Instead of changing the DataContext of your window, you should add a property to your MyBusinessObject class like this one:
private Widget _selectedWidget;
public Widget SelectedWidget
{
get { return _selectedWidget; }
set
{
if (_selectedWidget == value)
{
return;
}
_selectedWidget = value;
OnPropertyChanged(new PropertyChangedEventArgs("SelectedWidget"));
}
}
Then bind SelectedWidget to the SelectedItem property of your combobox. Anywhere that you need to use the widget's properties you can do this:
<TextBox Text="{Binding Path=SelectedWidget.WidgetColor}"></TextBox>
try
<TextBox Text="{Binding MyBusinessObject.MyPropertyName}"></TextBox>
this works if MyBusinessObject is the datacontext of the textbox and MyPropertyName is a property of MyBusinessObject
Also, Here is a good article to clarify binding
hope this helps
EDIT 1:
use a relative binding like this:
text="{Binding DataContext.MyPropertyName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TypeOfControl}}}"
So the relatve binding allows you to look up the visual tree to another UI element and use its datacontext. I would consider wrapping your window's contents in a grid. and wet your windows datacontext to the businessobject and the grids datacontext to the widget. That way you can always use the parent window's datacontext through the realtive source binding.
so use the following if your window's datacontext is your business object
text="{Binding DataContext.MyPropertyName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
I want to bind a TextBox in the window to a property contained within a class that is a variable of the viewmodel and ensure that INotifyPropertyChanged's PropertyChanged event propagates from the class to the parent.
Let me illustrate with an example:
(Window's DataContext is set to an instance of ViewModel)
public class ViewModel {
private OtherClass classInstance = new OtherClass();
public int Attribute {
get { return classInstance.Attribute; }
}
}
public class OtherClass : INotifyPropertyChanged {
private int _attribute;
public int Attribute {
get { return _attribute; }
set {
_attribute = value;
PropertyChanged("Attribute");
}
}
...
}
The problem in this example is that, when Attribute changes, the bound Textbox does not update the binding since I assume it's listening to the ViewModel's PropertyChanged event and not that of the instance of OtherClass.
Any ideas on how to remedy this situation? I was thinking about chaining OtherClass's INotifyPropertyChanged to that of its parent, but there has to be a better way.
Why not bind directly to the class property instead of using a proxy?
public class ViewModel {
private OtherClass classInstance = new OtherClass();
public OtherClass MyOtherClass {
get { return classInstance; }
}
}
Then in your binding you can simply reference the property via the SubClass
{Binding MyOtherClass.Attribute}
A drop dead simple example, but it works!
The Code Behind:
public partial class MainWindow : Window {
private readonly SomeClass _someClass = new SomeClass();
public MainWindow() {
InitializeComponent();
DataContext = _someClass;
}
}
public class SomeClass: INotifyPropertyChanged {
private readonly SomeSubClass _mySubClass = new SomeSubClass();
public SomeSubClass MySubClass {
get { return _mySubClass; }
}
private String _name;
public String Name {
get { return _name; }
set {
_name = value;
OnPropertyChanged("Name");
}
}
//Property Change Stuff
}
public class SomeSubClass : INotifyPropertyChanged {
private String _name;
public String Name {
get {
return _name;
}
set {
_name = value;
OnPropertyChanged("Name");
}
}
//Property Change Stuff
}
The XAML:
<Window x:Class="JWC.Examples.WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<StackPanel>
<Label Content="Name" VerticalAlignment="Top" />
<TextBox Text="{Binding Name}" />
<Label Content="SubClass.Name" />
<TextBox Text="{Binding MySubClass.Name}" />
<Label Content="Bound to Name" />
<TextBlock Text="{Binding Name}" />
<Label Content="Bound to MySubClass.Name" />
<TextBlock Text="{Binding MySubClass.Name}" />
</StackPanel>
</Window>
You will need to do something like this:
public class ViewModel {
public ViewModel() {
classInstance = new OtherClass();
classInstance.PropertyChanged += HandleAttributeChanged;
}
private void HandleAttributeChanged(object Sender, NotifyPropertyChangedEventArgs args) {
PropertyChanged("Attribute");
}
}
I don't show it here, but you should also implement IDisposable and unsubscribe from the PropertyChanged event on your child, otherwise you will leak memory.
Alternatively you can expose the classInstance as a public property and set your binding to: ViewModel.classInstance. Note this needs to be a property and not the field itself.
I think the parent class should subscribe to the child propertyCahnged event..something like:
public class ViewModel
{
private OtherClass classInstance = new OtherClass();
public ViewModel()
{
classInstance.PropertyChanged += NotifyChildAttributeChanged;
}
public int Attribute
{
get { return classInstance.Attribute; }
}
}
NotifyChildAttributeChanged is basically a method that listens only to the "Attribute" property and fires a PropertyChanged notification of its own..
Of course our parent class must implement INotifyPropertyChanged as well as will all ViewModels (preferably) and your DataContext will detect the change.
To get around this you need to implement INotifyPropertyChanged on your view model as well. Just add the interface and the event and the rest will take care of itself - no need to chain the events / calls together.
Check out this for using reflection to get the property as well.
http://tsells.wordpress.com/2011/02/08/using-reflection-with-wpf-and-the-inotifypropertychanged-interface/
I have a WPF application using MVVM; when I change the ViewModel in my main window ViewModel class, the new user control is not displayed in the window... the original one remains. The ViewModel looks like this:
public class MainWindowViewModel : ViewModelBase
{
public ViewModelBase Workspace;
public MainWindowViewModel()
{
var w = new CustomerDetailsViewModel();
SetActiveWorkspace(w);
}
void NavigationService_ViewChanged(object sender, ViewChangedEventArgs e)
{
SetActiveWorkspace(e.View);
}
void SetActiveWorkspace(ViewModelBase workspace)
{
Workspace = workspace;
}
}
My XAML looks like this:
< ContentControl Content="{Binding Path=Workspaces}" >
The navigation service ViewChanged event is firing, and the SetActiveWorkspace method is being called with the correct view in the argument. However, after that, the view is not reloaded. What am I missing here?
Your Workspace property is not raising the PropertyChanged event. It should look like this:
private ViewModelBase _workspace;
public ViewModelBase Workspace
{
get { return _workspace; }
set
{
if (value != _workspace)
{
_workspace = value;
// This raises the PropertyChanged event to let the UI know to update
OnPropertyChanged("WorkSpace");
}
}
}
Make sure your ViewModelBase implements INotifyPropertyChanged