I'm binding my viewmodel and view using resource dictionary as follows
<DataTemplate DataType="{x:Type viewmodels:MyViewModel}">
<Views:MyView />
</DataTemplate>
in MyView, i have dataGrid x:Name="BoxDataGrid" with DataGrid.RowDetailsTemplate having other dataGrid x:Name="SpoolsDataGrid"
how to access MyView or datagrids above using code behind in MyViewModel ?
The reason is,i want to load and show contents inside RowDetailsTemplate only when main datagrid row selected (clicked) thru event "RowDetailsVisibilityChanged".
Thanks.
Correction:
My bad. I want to access MyView not MyViewModel
It's quite easy. DataContext property in your MyView object points to concrete object of MyViewModel. So you can use XAML bindings to this view-model or access in code-behind e.g.
MyViewModel model = (MyViewModel) DataContext;
asktomsk's answer is correct. You can access the ViewModel via the DataContext property.
However, with a little bit more effort you can almost always get around directly accessing the ViewModel from the view. The whole point of MVVM or MVC is that there aren't dependencies from View to ViewModel.
Things you should research in WPF for MVVM include:
Attached Properties
Attached Behaviors
Mediators
Value Converters
Markup Extensions
You need to be aware of all of these to find elegant solutions to some problems you encounter with MVVM.
You'll need to specify a bit more detail about the behaviour you are trying to get if you want us to help you figure out how you can do it without accessing the ViewModel through the datacontext.
You can, for instance, bind somethings' Visibility to a boolean in the ViewModel using a converter?
I apologize if you know all of the above already.
Just solved this problem using MVVM Light Toolkit - EventToCommand. Other better suggestions are very much welcome.
http://blog.galasoft.ch/archive/2009/11/05/mvvm-light-toolkit-v3-alpha-2-eventtocommand-behavior.aspx
Hope this solution will be useful to others.
don't need to know the view, in my datagrid view
<i:Interaction.Triggers>
<i:EventTrigger EventName="RowDetailsVisibilityChanged">
<cmd:EventToCommand Command="{Binding RowDetailsVisibilityChangedCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
and in viewmodel
public RelayCommand<DataGridRowDetailsEventArgs> RowDetailsVisibilityChangedCommand
{
get;
private set;
}
and in viewmodel constructor
RowDetailsVisibilityChangedCommand = new RelayCommand<DataGridRowDetailsEventArgs>(e =>
{
DataGrid SpoolsDataGrid = e.DetailsElement as DataGrid;
DataRowView drv = (DataRowView)e.Row.Item;
serialNo = drv.Row["BOX_SERIAL"].ToString();
SpoolsDataGrid.ItemsSource = as400Service.GetSPOOL_BY_SERIAL_NO(serialNo);
});
Related
I am relatively new to WPF and I have stumbled across a problem that I just can't seem to find a solution for.
I am sure that there is already a thread concerning a problem like that but in regard of my lacking knowledge it is very likely that I haven't found it or simply did not understand it.
My problem:
I am developing a WPF-application in C#. It's an Outlook-Styled application with a big MainWindow with a huge ViewModel and XAML.
What I was trying to do, is to split up the single codefiles a bit to make it a little bit more modular and compact.
I am using Telerik Controls and tried to outsource the content of single SplitContainers into Pages, which worked fine until now.
Today, a new situation came up which is somehow stupid and wasn't looking too complicated, but somehow I can't get it to work.
Situation:
I have a Treeview in my "MainWindow" and whenever I change the selection in there, I want to change a property on my Page that I have made a binding to.
So, when I click on an item in "TreeView_3" I want to set a property via EventHandler (SelectionChanged_TreeView3) on the DataContext of "Page_X".
If I had to do this on the MainWindow, I would typically do it like that:
UserViewModel uvm = mainGrid.DataContext as UserViewModel;
Then just call whatever property of specific UserViewModel (ViewModel of the MainWindow) I want to access.
I can't do this the same the same way for the page obviously since "mainGrid.DataContext" will always refer to the MainWindow, since this is where the eventhandler is called.
So what I need would be a little explanation on how to access the DataContext from a page with a different ViewModel.
If you need any code in order to explain, let me know.
You need to separate your concerns. In your code behind your should have only code that handles view related stuff. Most often my codebehind is empty.
In your ViewModels you should handle your data related logic. So instead of casting the datacontext in your code behind, handle a click with a Commandin your viewmodel.
Since there is no possibility to bind a command to the SelectedItemChanged of your TreeView you can use an interaction trigger.
<TreeView xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding Path=SomeCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
Ruven it is hard to say without some example code. But it could be that you need to implement INotifyPropertyChanged on the ViewModels?
https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-property-change-notification
By calling OnPropertyChanged("PropertyName"); in the setter of a viewmodel property the ui will pick up the change.
Also make sure both views are referencing the same object and not copies of the same object.
I have the following code in my C# WPF MVVM application.
public RelayCommand PolishCommand
{
get
{
polishcommand = new RelayCommand(e =>
{
PolishedWeightCalculatorViewModel model = new PolishedWeightCalculatorViewModel(outcomeIndex, OutcomeSelectedItem.RoughCarats);
PolishedWeightCalculatorView polish = new PolishedWeightCalculatorView(model);
bool? result = polish.ShowDialog();
if (result.HasValue)
{
But i came to know that, calling a window from viewmodel is wrong one in MVVM pattern.
Also stated in the below link.
M-V-VM Design Question. Calling View from ViewModel
Please help me anybody by providing an alternate solution.
Thanks in advance.
You are right that generally you should never access views from view models. Instead in WPF, we set the DataContext property of the view to be an instance of the relating view model. There are a number of ways to do that. The simplest but least correct is to create a new WPF project and put this into the constructor of MainWindow.xaml.cs:
DataContext = this;
In this instance the 'view model' would actually be the code behind for the MainWindow 'view'... but then the view and view model are tied together and this is what we try to avoid by using MVVM.
A better way is to set the relationship in a DataTemplate in the Resources section (I prefer to use App.Resources in App.xaml:
<DataTemplate DataType="{x:Type ViewModels:YourViewModel}">
<Views:YourView />
</DataTemplate>
Now wherever you 'display' a view model in the UI, the relating view will automatically be shown instead.
<ContentControl Content="{Binding ViewModel}" />
A third way is to create an instance of the view model in the Resources section like so:
<Window.Resources>
<ViewModels:YourViewModel x:Key="ViewModel" />
</Window.Resources>
You can then refer to it like so:
<ContentControl Content="{Binding Source={StaticResource ViewModel}}" />
I have answered a very similar question previously, which details how you can open a new window from your view model, whilst maintaining the separation of concerns that the MVVM pattern promotes. I hope this helps: Open a new Window in MVVM
You are allowed to break the rule. You don't have to follow MVVM completely.
I am always using commands to create a new view. You could even create an event (Amagosh, did he just say that!?) for when you click on a button.
I mean, this is just my opinion, I guess it depends on the style programming you're into.
I've been following this tutorial to try to work an understanding of XML, WPF, and C# (coming out from Fortran). I've gotten the functionality working (thanks to the tutorial), but I'm having troubles modifying it to work with WPF instead of WinForms.
Essentially, when I click the "Get RSS" button, the following is happening:
RssManager reader = new RSSManager();
ObservableCollection<Rss.Items> _list = new ObservableCollection<Rss.Items>();
reader.Url = txtFeed.Text;
reader.GetFeed();
_list = (ObservableCollection<Rss.Items>)reader.RssItems;
The listview just sits blank. It's code is like the following. Also, trying this with a listbox results in the name of the class being populated for each item in the list instead of my data:
<ListView ItemsSource="_rssItems">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Title}"/>
</GridView>
</ListView.View>
</ListView>
Would it be possible to do something like (again, forgive me for my ignorance, here):
_list.Add( new Rss.Items());
The list (_list) contains all of the information that I need, I just want to figure out how to properly bind it (or add it) to the ListView.
It looks like you are a bit lost.
Ultimately you want to bind your view(WPF form) to a View-Model and your View-Model to a model (the RSSManager).
For now lets bind the view directly to the model.
In your constructor you make a new instance of the model and you assign it to the data context.
This model is going to live as long as the form -
public MainWindow()
{
InitializeComponent();
_model = new RssManager();
DataContext = _model;
}
Then in your XAML you bind the item source to your collection property :
<ListView ItemsSource="{Binding Path=RssItems}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Title}"/>
</GridView>
</ListView.View>
</ListView>
Note that in "Path=RssItems" is relative to whatever you assigned to the DataContext.
Then in your refresh button logic you call:
_model.Url = txtFeed.Text;
_model.GetFeed();
What you ultimately want to do is put another layer in the middle. This is the view model. The View model is as you may have guessed a model of the view.
The view model's job is to collect information about the state of the view and to expose the data from the model that is to be presented by the view. It also can hold current ui state information - f.e. which row in the table is selected, since some command may act on that later. In essence it allows to abstract all the logic of the view from your code. Your commands operate on things like which row is selected - regardless of which type of control did the selection.
As Lee suggests in his comment, the binding for the ItemsSource property of the ListView does not appear to be correct. There are multiple ways to approach this, depending on how your project is designed/structured.
In order to DataBind, the ListView will need some kind of DataContext which (as the name implies) is kind of the Context for the Binding. If you are using MVVM, then most likely, the DataContext of your entire Window/Control would be a ViewModel. In that case, you bind to a property of the ViewModel like this:
<ListView ItemsSource="{Binding RssItems}">...</ListView>
This assumes you have a ViewModel with a public RssItems property (which is some kind of List/Enumerable) and the ViewModel is DataContext.
If you are not using MVVM, there are a lot of ways to assign the ItemsSource both with DataBinding and without. The easiest way I can suggest, if you're not fully comfortable with DataBinding, would be to manually assign the ItemsSource, like this:
Xaml:
<ListView x:Name="MyRssList">...</ListView>
Code Behind (somewhere after the UI has Loaded and after you've created/populated _list):
MyRssList.ItemsSource = _list;
This doesn't use DataBinding, but it will get the job done. If you want to start out with DataBinding, you could do the following:
XAML:
<ListView x:Name="MyRssList" ItemsSource="{Binding}>...</ListView>
Code Behind:
MyRssList.DataContext = _list;
This will assign the List as the DataContext of the ListView, then DataBind the ItemsSource property to the DataContext.
Overall, You're on the right track. I'd recommend some reading on DataBinding and MVVM. MVVM is a very good way to leverage the powerful DataBinding capabilities of WPF, and a strong understanding of DataBinding is extremely valuable in designing and building great WPF apps.
Hope this helps. Good luck!
This is a question that extends from the originally posted here:
Link to loading-xaml through runtime
I'm working on a WPF MVVM application that loads XAML content dynamically from an external source, very similar as the answer in the post above.
Here is what I got so far:
My View declares an instance of the ViewModel as a resource and creates an instance of that ViewModel
In my ViewModel constructor I'm loading a XamlString property coming from an external source (file or db..)
In my view I have a button that user clicks after ViewModel finishes loading and in the click-event code-behind I'm deserializing the dynamically loaded XAML and add it to my grid.
My question is, how can I eliminate code-behind and automate the logic so the View can render the new xaml section dynamically right after the ViewModel is done getting the XAML content and initializing the string property?
Should I use some kind of Messaging Bus so the ViewModel notifies once the property has been set so the View can add the new content?
What troubles me is the fact that ViewModels do have a reference to Views and should not be in charge of generating UI elements.
Thanks in advance!
Edit:
Just to clarify: in my particular case I am not trying to bind a Business Object or Collection (Model) to a UI element (e.g. Grid) which obviously could be accomplished through templates and binding. My ViewModel is retrieving a whole XAML Form from an external source and setting it as a string property available to the View. My question is: Who should be in charge of deserializing this XAML string property into a UI element and add it programmatically to the my grid once my Xaml string property in the VM is set?
This sounds to me more of like a View responsibility, not ViewModel. But the pattern as i understand it enforces to replace any code-behind logic with V-VM bindings.
I have a working solution now and I'd like to share it. Unfortunately I did not get rid of code-behind completely but it works as I expect it to. Here is how it works(simplified):
I have my simplified ViewModel:
public class MyViewModel : ViewModelBase
{
//This property implements INPC and triggers notification on Set
public string XamlViewData {get;set;}
public ViewModel()
{
GetXamlFormData();
}
//Gets the XAML Form from an external source (e.g. Database, File System)
public void GetXamlFormData()
{
//Set the Xaml String property
XamlViewData = //Logic to get XAML string from external source
}
}
Now my View:
<UserControl.Resources>
<ViewModel:MyViewModel x:Key="Model"></ViewModel:MyViewModel>
</UserControl.Resources>
<Grid DataContext="{StaticResource Model}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel>
<!-- This is the Grid used as a Place Holder to populate the dynamic content!-->
<Grid x:Name="content" Grid.Row="1" Margin="2"/>
<!-- Then create a Hidden TextBlock bound to my XamlString property. Right after binding happens I will trigger an event handled in the code-behind -->
<TextBlock Name="tb_XamlString" Text="{Binding Path=XamlViewData, Mode=TwoWay, UpdateSourceTrigger=LostFocus, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Visibility="Hidden" Loaded="tb_XamlString_Loaded" />
</StackPanel>
</Grid>
Basically I created a hidden TextBlock bound to my XAML String property in the ViewModel and I hooked its Loaded event to an event handler in the code behind of the View:
private void tb_XamlString_Loaded(object sender, RoutedEventArgs routedEventArgs)
{
//First get the ViewModel from DataContext
MyViewModel vm = content.DataContext as MyViewModel;
FrameworkElement rootObject = XamlReader.Parse(vm.XamlViewData) as FrameworkElement;
//Add the XAML portion to the Grid content to render the XAML form dynamically!
content.Children.Add(rootObject);
}
This may not be the most elegant but gets the job done. Like some people say, in MVVM there are some cases like this where little code-behind code is needed. It doesn't hurt and also part of this solution still uses the V-VM Binding principles when using the VM to retrieve and populate the XamlString property and exposing it to the View. If we would like to Unit Test the XAML parsing and loading functionality we could delegate it to a separate class.
I hope someone finds this useful!
I'm having trouble understanding what you're saying, so my answer will be based on my interpretation. You should consider posting a sample (simplified) of what you're trying to do.
1) I think you're misunderstanding what MVVM does. MVVM is mostly a binding-based pattern. Your view model should be exposing properties containing business objects and your view should just be binding to those properties. If I am misunderstanding you, and that's what you are doing, then your problem is that your view needs to be aware of when the properties get updated (after you deserialize your xaml, etc). There are two ways to do this: INotifyPropertyChanged interface on your viewmodel, or make your view model inherit from DependencyObject, and make the properties dependency properties. I won't go into details here, because this is a large subject that you should research on Google before making a decision.
2) Generally speaking, you shouldn't use click events inside your view if you're using MVVM. Instead, create properties on the view model of type ICommand (and create ICommand implementations to match, or use an implementation of DelegateCommand (google it) which will allow you to use delegates to implement the interface. The idea is, your view binds to the property and executes the handler directly inside the viewmodel.
3) If you want to push information from the viewmodel to the view, then you should create an event on the viewmodel and subscribe to it in the view, but this is a last resort, only to be used in cases like displaying a new window, etc. Generally, you should be using binding.
4) To be more specific about what you're doing, you should be binding your Grid's ItemsSource property to some property on the view model. Note, the property on the view model should be of type ObservableCollection<T> if you want to be able to add items and get instant updates.
Hope this helps.
I've built a WPF UserControl View/ViewModel pair: the view XAML handles the layout and bindings, and the ViewModel handles the logic, in-line with the recommended MVVM pattern.
I would like to be able to re-use this as a control.
How do I hide/encapsulate the ViewModel associated with the view, so that I can use the control as I would a standard control [such as a button] ?
i.e. How do I hide the control's viewmodel ?
depends on how you bind ViewModel class to the control.
if you do like this:
YourControl()
{
DataContex = new ViewModel();
}
then I don't see any problems. add reference to your control and use it.
You can create your ViewModel as a StaticResource within your XAML. The problem with setting the DataContext to your ViewModel is that you can't use that you can no longer use your DataContext from the window or page you in which you use the control.
In your XAML declare your ViewModel:
<myNS:MyViewModel x:Key="ViewModel />
Reference your view model within your XAML:
<TextBlock Text="{Binding Source={StaticResource ViewModel}, Path=TextToBind}" />
In your Code Behind you can access and initialize quickly, I usually make a property for easy reference to my view model.
private MyViewModel viewModel
{
get { return this.Resources["ViewModel"] as MyViewModel; }
}