How to set a class in the DataContext with XAML? - c#

I have a WPF forms and a class Users (content attributes Id, Login and Name), in my class of this forms, I had get a Users object for put this information in the form with DataContext and Binding
I can put this Users object to my Window.DataContext (this.DataContext = usersObject;) with code behind, but I think if I can make this with XAML, maybe is better
I have set a attribute in my class UserForm (public Users usersObject {get; set;})
My form UserForm : Window
<Window DataContext="{What need I put here?">
<Grid>
<TextBlock Text="Id:"/>
<TextBox Name="Id" Text="{Binding Path=Id}"/>
<TextBlock Text="Login:"/>
<TextBox Name="Login" Text="{Binding Path=Login}"/>
<TextBlock Text="Name:"/>
<TextBox Name="Name" Text="{Binding Path=Name}"/>
</Grid>
</Window>
UserForm.xaml.cs
public class UserForm : Window
{
public Users userObject { get; set; }
public UserForm(Users user)
{
InitializeComponent();
this.userObject = user;
}
}
My class Users
public class Users
{
public int Id { get; set; }
public string Login { get; set; }
public string Name { get; set; }
}
How to I do for set userObject in the itself Window.DataContext for TextBox's can put it values?

Some time back I had written an article on different options of binding XAML control to code behind property. This might help you.
http://codingseason.blogspot.in/2013/05/data-binding-xaml-control-to-property.html

Remove the DataContext binding since you are not explicitly doing MVVM pattern. No point of doing the binding.
In your Window.xaml
<Window>
<Grid>
<TextBlock Text="Id:"/>
<TextBox Name="Id" Text="{Binding Path=Id}"/>
<TextBlock Text="Login:"/>
<TextBox Name="Login" Text="{Binding Path=Login}"/>
<TextBlock Text="Name:"/>
<TextBox Name="Name" Text="{Binding Path=Name}"/>
</Grid>
Add this to your Behind the code Window.xaml.cs
public class UserForm : Window
{
public Users userObject { get; set; }
public UserForm(Users user)
{
InitializeComponent();
this.DataContext = user;
}
}
If you would do it in MVVM you would have done something like this
ViewModel.cs
public class UserViewModel
{
private Users _model;
public UserViewModel(Users model)
{
_model = model;
}
public int Id { get { return _model.Id; } }
public string Login { get { return _model.Login; } set { _model.Login; } }
public string Name { get { return _model.Name; } set { _model.Name; } }
}
Now the ViewModel can be customize depending on what you need, you can expose the Model, inject it to the constructor or just set the property value if it is exposed. Don't forget to implement INotifyPropertyChanged if you want to propagate any values in the ViewModel back to the user interface.
View.xaml.cs
public class UserForm : Window
{
public UserForm(Users user)
{
InitializeComponent();
this.DataContext = new UserViewModel(user);
}
View.xaml
You have two choices, you can explicitly set the DataContext just like what I did behind the code or you can create a public property that returns the UserViewModel. It's just the same
Model.cs
public class Users { //Whatever properties you need }
Now this is a very simplistic example of MVVM pattern. Once you know the basics, you can then integrate some helpful frameworks that implements MVVM for you like Caliburn Micro and MVVM Toolkit

Create an object of UserForm and assigning data context is pretty simple.
UserForm userFormView = new UserForm ();
Users dataContextObject = new Users();
userFormView.DataContext = dataContextObject;
userFormView.Show() //you can also use showdialog. Thats up to you
Its better you remove the following code:
public Users userObject { get; set; }
public UserForm(Users user)
{
InitializeComponent();
this.userObject = user;
}
Its better to separate the view and the viewmodel. Have a look at MVVM and it will make more sense

This is one of the way to assign DataContext directly from xaml.
Example App.xaml
<Application.Resources>
<local:Users x:Key="Users"/>
</Application.Resources>
Example UserForm.xaml
<Window DataContext="{StaticResource Users}">
<Grid>
<TextBlock Text="Id:"/>
<TextBox Name="Id" Text="{Binding Path=Id}"/>
<TextBlock Text="Login:"/>
<TextBox Name="Login" Text="{Binding Path=Login}"/>
<TextBlock Text="Name:"/>
<TextBox Name="Name" Text="{Binding Path=Name}"/>
</Grid>
</Window>

Maybe you can define a DependencyProperty to wrap the access to the userObject,
then bind it to the DataContext

Related

WPF / XAML: Data Binding for UserControls

So I'm creating "cards" to visually represent a collection of objects within a StackPanel (which I'm using a list to hold these objects):
MainWindow XAML:
<Window /* ... */>
<StackPanel x:Name="Deck" Orientation="Horizontal" />
</Window>
MainWindow C#:
public partial class MainWindow : Window
{
/* ... */
private void OnDataReceived(List<Reading> readings)
{
foreach(Reading r in readings)
{
Deck.Children.Add(new Card
{
Id = r.Id,
Value = r.Value
});
}
}
}
UserControl XAML:
<UserControl /* ... */ x:Name="crd">
<Label Content="{Binding Path=Id, ElementName=crd}" />
<Label Content="{Binding Path=Value, ElementName=crd} />
</UserControl>
UserControl C#:
public partial class LoggerRepresentation : UserControl
{
public string Id { get; set; }
public int Value { get; set; }
/* ... */
}
Upon adding one element to Deck.Children, its' visual representation does appear in the StackPanel as expected. However, DP seems to lack something as the Labels binding Id and Value remain empty.
(The idea to give x:Name="crd" to my UserControl and then use it within the Binding as ElementName has been plugged from the answer to a seemingly related question, but may be misleading in this case.)
You should use an ItemsControl whenever you want to display a dynamic collection of items.
Replace the StackPanel in your XAML markup with an ItemsControl:
<ItemsControl ItemsSource="{Binding Cards}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:LoggerRepresentation />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Bind directly to the source properties of the corresponding Card object in the UserControl (and replace the Label elements with TextBlocks):
<TextBlock Text="{Binding Path=Id}" />
<TextBlock Text="{Binding Path=Value} />
Create a view model class that exposes a collection of Card objects through a public property and move your application logic over to this one:
public class ViewModel
{
public ObservableCollection<Card> Cards { get; } = new ObservableCollection<Card>();
//...
private void OnDataReceived(List<Reading> readings)
{
foreach (Reading r in readings)
{
Cards.Add(new Card
{
Id = r.Id,
Value = r.Value
});
}
}
}
Set the DataContext of the view where the ItemsControl is defined to an instance of the view model class:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
This design pattern is known as Model-View-ViewModel (MVVM) and it's the recommended pattern to use when developing XAML based UI applications. You will find a lot more to read and learn about it if your Google.

How do you take input of a TextBox and return it in a TextBlock

I am new to MVVM and I am trying to type a string into a textbox and it return on a textblock on another page.
In my Views folder I have this code in xaml which is the textbox that I want to type into:
<TextBox x:Name="date" Text="{Binding Date}" Grid.Row="0" TextAlignment="Right" TextWrapping="Wrap" Margin="0 10 0 1" Padding="1" />
This is a different wpf page that has the textblock and I want what was typed in the textbox to appear here:
<TextBlock Grid.Row="0" TextAlignment="Right" TextWrapping="Wrap" Margin="0 0 0 2" Padding="1" Text="{Binding Date}" />
In my Model folder I have the class Data Entry which looks like this:
public class DataEntry
{
public string Date { get; set; }
}
In my ViewModels folder I have:
namespace FumeHood1._0._0.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
public DataEntry DataEntry { get; set; }
private string date;
public string Date
{
get { return date; }
set
{
date = value;
OnPropertyChanged(nameof(Date));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I have been looking everywhere and I cant find the right way to do it. If anyone could help it would be amazing. Just trying to make this MVVM pattern work and make more sense to me.
First, set the DataContext of your view.
public partial class MainWindow : Window
{
public MainWindow() {
InitializeComponent();
DataContext = new MainViewModel();
}
}
Or in xaml : (NOT both at the same time)
<Window>
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
</Window>
And make sure the same instance of MainViewModel is used in both pages.
Second, carefully configure your bindings to act as intended:
<TextBox Text="{Binding Date, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"/>
<TextBlock Text="{Binding Date}" />
Note that UpdateSourceTrigger=PropertyChanged makes sure that the view model property is updated while the user types. Mode=OneWayToSource only updates the view model property from the TextBox Text property, but not the other way round.

How do I project models into viewmodels in XAML?

Given the following classes:
public class Neighborhood
{
public IEnumerable<House> Houses { get; set; }
}
public class House
{
public Address Address { get; set; }
public IEnumerable<Room> Rooms { get; set; }
}
public class Room
{
public IEnumerable<Furniture> Furniture { get; set; }
}
I want views that look something like this:
<!-- Want DataContext to be a NeighborhoodViewModel, not a Neighborhood -->
<NeighborhoodView>
<ListBox ItemsSource={Binding Houses}/>
<Button Content="Add House"/>
<Button Content="Remove House"/>
</NeighborhoodView>
<!-- Want DataContext to be a HouseViewModel, not a House-->
<HouseView>
<TextBox Text={Binding Address}/>
<ListBox ItemsSource={Binding Rooms}/>
<Button Content="Add Room"/>
<Button Content="Remove Room"/>
</HouseView>
<!-- Want DataContext to be a RoomViewModel, not a Room -->
<RoomView>
<ListBox ItemsSource={Binding Furniture}/>
<Button Content="Add Furniture"/>
<Button Content="Remove Furniture"/>
</RoomView>
However, I don't want NeighborhoodViewModel to contain HouseViewModels. Rather, it should be like:
public class NeighborhoodViewModel
{
public IEnumerable<Room> Rooms { get; }
public ICommand AddHouseCommand { get; }
public ICommand RemoveHouseCommand { get; }
}
How can I declare bindings to models in XAML, but have the bindings be transformed into viewmodels?
There are two general ways you can create this type of effect. The first way is to create a static view and put a DataContext behind each view. This is not MVVM where the views are generated by the ViewModel but this will get your bindings to work. This is an example of view.
<Grid>
<HouseView x:Name="myHouse">
</HouseView>
</Grid>
In the code behind you can get access to your HouseView and set the data context
public MainWindow()
{
myHouse.DataContext = new MyHouseViewModel();
}
This will get your bindings to work for each one of these controls.
I will say that this is not the best practice of WPF and it is certainly not a way to develop large projects. However this will do for quick and dirty coding in my opinion. You can find out more on how to do proper MVVM style of coding here.

MVVM Binding Observable Collection to view?

In my application, I need to bind a checkbox list to an observable collection. I have seen many examples but I could not find a proper implementation for this and thats why I am posting this question.
The View:
<Grid Name="GrdMain" Background="White">
<ListView Name="lstConditions" VerticalAlignment="Top" Height="150"
ItemsSource="{Binding ConditionsModels}" Margin="0,25,0,0" BorderBrush="Transparent" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding Path=condition}" Margin="8" Style="{StaticResource CheckBoxDefault}"
IsChecked="{Binding hasCondition,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListView>
</grid>
The model:
public class ConditionsModel
{
public int profileId { get; set; }
public string condition { get; set; }
public bool hasCondition { get; set; }
}
The View Model:
public class ConditionsViewModel : INotifyPropertyChanged
{
private ConditionsModel _conditionsModel;
private ObservableCollection<ConditionsModel> _conditionsModels;
public ConditionsModel ConditionsModel
{
get
{
return _conditionsModel;
}
set
{
_conditionsModel = value;
RaisePropertyChanged("ConditionsModel");
}
}
public ObservableCollection<ConditionsModel> ConditionsModels
{
get
{
return _conditionsModels;
}
set
{
_conditionsModels = value;
RaisePropertyChanged("ConditionsModels");
}
}
public ConditionsViewModel(int profileId)
{
ConditionsModel = new ConditionsModel();
ConditionsModels = new ObservableCollection<ConditionsModel>();
ConditionsModels.CollectionChanged += ConditionsModels_CollectionChanged;
GetConditions(profileId);
}
void ConditionsModels_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged("ConditionsModels");
}
private void GetConditions(int profileId)
{
HealthAssessmentRepository _rep = new HealthAssessmentRepository();
_conditionsModels = _rep.GetConditions(profileId);
}
}
Is this a correct implementation? I need to update the model when the user checks or unchecks the checkbox. But its not raising the propery changed event when the check box is checked or unchecked.Should I implement the INotifyPropertyChanged interface on the model as well?
I have seen many examples, but all of them has different approaches to this and I am confused. Please show the correct implementation of this?
Thanks
I think you have missed the DataType property within DataTemplate. Just refer this
<DataTemplate DataType="{x:Type sampleApp:ConditionsModel}">
Here sampleApp in the namespace reference created within tag. And ConditionsModel is your model class.
You need to implement INotifyPropertyChanged for class ConditionsModel and raise PropertyChangedEvent for the property you want to observe/synchronize, because it is ViewModel as well.
For class ConditionsViewModel, it's the ViewModel of whole ListView, for ConditionsModel, it's the ViewModel of every line. ViewModel can be overlaid. If ConditionsModel is the domain model, my suggestion is that add a new ItemViewModel, because they belong to different layers. It's always better to distinguish the different layers properly.

MVVM Light: How to get data from usercontrols?

I have a class MyDataCollection that contains MyMetaData and MyData. In my application i have two usercontrolls that display input fields to the user. One for the MyMetaData and the other for MyData. Both usercontrols are included in the MainPage.
My Question is: How should i get the data from the usercontrols then the user klicks on the save-button (located on the mainpage)?
Update
I have changed my code accordingly to blindmeis post but now the MetaDataView is not shown:
<UserControl.Resources>
<DataTemplate x:Key="MetaDataTemplate">
<view:MetaDataView/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ContentPresenter Content="{Binding MetaDataTemplate}"/>
</Grid>
why not doing mvvm the easy way?(viewmodel first). you say you have a mainpage - this means you also have a mainpageviewmodel. your mainpageviewmodel handles at least the save command. now you want to show MyMetaData and MyData on your mainpage. so the easy way would be to create one MyMetaData instance and one MyData instance in your mainviewmodel.
public class MainPageViewmodel
{
public ICommand SaveCommand { get; set; }
public MyDataViewmodel MyData { get; set; }
public MyMetaDataViewmodel MyMetaData { get; set; }
public MainPageViewmodel()
{
this.MyData = new MyDataViewmodel();
this.MyMetaData = new MyMetaDataViewmodel();
}
}
public class MyDataViewmodel
{}
public class MyMetaDataViewmodel
{}
your mainpage just need 2 datatemplates and 2 contentpresenter.
//resources
<DataTemplate DataType="{x:Type Local:MyDataViewmodel}">
<view:MyDataUserControl/>
</DataTemplate>
<DataTemplate DataType="{x:Type Local:MyMetaDataViewmodel}">
<view:MyMetaDataUserControl/>
</DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ContentPresenter Content="{Binding MyData}" Grid.Column="0"/>
<ContentPresenter Content="{Binding MyMetaData}" Grid.Column="1"/>
<Button Content="Save" Command="{Binding SaveCommand}" Grid.Column="2"/>
</Grid>
because your mainpageviewmodel has both "child" viewmodel, you have all information you want on your savecommand.
if you have another scenario pls update your question, maybe post some code.
EDIT: i have no silverlight so that just a suggestion: maybe rachel can give you a better answer.
<Grid>
<ContentPresenter Content="{Binding MyMetaData}" ContentTemplate="{StaticResource MetaDataTemplate}"/>
</Grid>
if silverlight cant handle datatemplates with datatype you could just put the usercontrol there directly.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<view:MyDataUserControl DataContext="{Binding MyData}" Grid.Column="0"/>
<view:MyMetaDataUserControl DataContext="{Binding MyMetaData}" Grid.Column="1"/>
<Button Content="Save" Command="{Binding SaveCommand}" Grid.Column="2"/>
</Grid>
Since you tagged this question as MVVM, your ViewModels should contain both your SaveCommand and all the data needed to perform the actual save
Your MainViewModel should contain MyMetaData and MyData properties (which are bound to their respective UserControls), and each of those objects should contain properties for any data needed in the UserControl. For example, if your UserControl had a TextBox for Name, then your data object should have a property for the Name that the TextBox binds to.
If the Save button is located in one of those UserControls then the respective ViewModel should have a SaveCommand that gets executed when the Button is clicked. All the data needed for the Save is also located in that ViewModel, so you're good to go.
If your MainViewModel is in charge of saving the data, then it should be able to hook into your sub ViewModel's SaveCommand and attach it's own method, such as
this.MyData.SaveCommand = this.SaveCommand();
and all the data needed for the save can be found in this.MyData
If the SaveButton is located in your MainView, and not in one of the UserControls, then the SaveCommand should be part of MainViewModel, and all the data needed for the save can be found in this.MyData or This.MyMetaData.
Remember, with MVVM your ViewModels are your application. The View is just a pretty interface that allows users to interact with your ViewModels.
You should use Two-way bindings to automatically update the value in your controller. Take a look at this article.
Here's an example:
<TextBox Text="{Binding MyMetaData, Mode=TwoWay }" />
<TextBox Text="{Binding MyData, Mode=TwoWay }" />
I'll give you a little sample, how you can use the MVVM Light Messenger for ViewModel-to-ViewModel communication. Say you have an MyDataCollection class:
public class MyDataCollection
{
public int MyData;
public string MyMetaData;
}
On your MainViewModel you have a RelayCommand (from MVVM light toolkit) binded by your View's SaveButton. When the Connad is executed, you will have to send a Message with a callback action to request data from the subcriber. The callback takes MyDataCollection as parameter:
public class MainViewModel : ViewModelBase
{
public RelayCommand SaveCommand { get; private set; }
//Ctor
public MainViewModel()
{
SaveCommand = new RelayCommand(
() =>
Messenger.Default.Send<NotificationMessageAction<MyDataCollection>>(
new NotificationMessageAction<MyDataCollection>("SaveData", SaveCallback)));
}
private void SaveCallback(MyDataCollection dataCollection)
{
// process your dataCollection...
}
}
The UserControlViewModel has properties the InputTextBoxes are binded too. It just has to register to the message and call the callback with data properties:
public class UserControlViewModel : ViewModelBase
{
//Properties
public string UserControlMetaData { get; set; }
public int UserControlData { get; set; }
//Ctor
public UserControlViewModel()
{
Messenger.Default.Register<NotificationMessageAction<MyDataCollection>>(this, MessageReceived);
}
// private Method to handle all kinds of messages.
private void MessageReceived(MessageBase msg)
{
if(msg is NotificationMessageAction<MyDataCollection>)
{
var actionMsg = msg as NotificationMessageAction<MyDataCollection>;
if(actionMsg.Notification == "SaveData") // Is this the Message, we are looking for?
{
// here the MainViewModels callback is called.
actionMsg.Execute(new MyDataCollection() {MyData = UserControlData, MyMetaData = UserControlMetaData});
}
}
}
}
You will have to use messengers or you will have to set the properties over the ViewModelLocator
Messenger example of how I use it to set the UI language
ViewModel A, I register a listener here with the "SetLanguage" token:
Messenger.Default.Register<string>(this, "SetLanguage", false, input =>
{
SetLanguage(input);
});
ViewModel B, here I send the message with the "SetLanguage" token:
Messenger.Default.Send("en-EN", "SetLanguage");
ViewModelLocator example in ViewModel A, I access data in ViewModel B over the locator:
short value = 12;
var myFilteredDataList = ViewModelLocator.ViewModelBStatic.MyDataList.Any(m => m.code == value);
I have two solutions now:
View:
<ContentPresenter Content="{Binding MyMetaDataView}" />
ViewModel:
public MetaDataViewModel MyMetaDataViewModel { get; set; }
public MetaDataView MyMetaDataView { get; set; }
public MainViewModel()
{
MyMetaDataViewModel = new MetaDataViewModel();
MyMetaDataView = new MetaDataView();
MyMetaDataView.DataContext = MyMetaDataViewModel;
}
or ----
View:
<UserControl.Resources>
<DataTemplate x:Key="MetaDataViewTemplate">
<view:MetaDataView />
</DataTemplate>
</UserControl.Resources>
...
<ContentPresenter Content="{Binding MyMetaDataViewModel}" ContentTemplate="{StaticResource MetaDataViewTemplate}"/>
ViewModel:
public MetaDataViewModel MyMetaDataViewModel { get; set; }
public MainViewModel()
{
MyMetaDataViewModel = new MetaDataViewModel();
}

Categories