I have ObservableCollection that contains some elements.In some Views to this collection binded some elements. But one element is special, and this element must not displaying in other Views. Because I want have other collection dependent on first collection. Of course, I can add elements to first and two other collections in my ViewModel, but first collection changed in many places. So, from CollectionChanged event I can't modify second collection. How can I make one ObservableCollection dependent on other?
I hope I understood your problem correctly :)
I don't think you need a second/third/... ObservableCollection. Extend your view model with a SpecialVisibility property (the name is up to you) of type System.Windows.Visibility. Then, bind this property to the Visibility property of your data template.
<ItemsControl.ItemTemplate>
<DataTemplate>
<YourControl Visibility="{Binding Path=SpecialVisibility}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
These 'special' elements (i.e. the elements which should not be dispayed) just need the SpecialVisibility set to System.Windows.Visibility.Collapsed.
Of course, this is not a solution to your problem of synchronizing many ObservableCollections. However, I think this might solve your problem. If using the CollectionChanged event is not an option for you, you might want to restructure your code.
You can create a new class that inherits ObservableCollection<T> that has a reference to the original collection. This new class subscribes to the CollectionChanged event of the original and reacts to it by changing itself.
Related
I am using CodePlex wpfmdi container for my WPF application.
I need to bind MdiContainer's children to a viewModel property.
<mdi:MdiContainer Name="Container" Grid.Row="1" Background="GhostWhite" Children="{Binding Path=Container}"/>
If I do this I am getting this error:
Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.Collections.ObjectModel.ObservableCollection`1[WPF.MDI.MdiChild]'
This is what the Children property in MdiContainer looks like:
public ObservableCollection<MdiChild> Children { get; set; }
What am I doing wrong?
The Children property is not exposed as a dependency property, which means you cannot bind it. Furthermore, it is initialized once in the constructor of the MdiContainer type and then a handler is added to the CollectionChanged event of the underlying ObservableCollection<MdiChild>. It is never updated or removed.
Therefore, although the Children property has a setter, it will break the control if you use it to set a different collection. This also implies that you cannot simply create attached properties to expose a bindable Children dependency property.
Apart from that, MdiChild is a Control, so it actually contradicts the purpose of your view model. If you expose a collection of user interface controls from your view model this conflicts with the MVVM pattern. View models should not have any knowledge about the view. However, the MDI controls do not seem to follow the usual WPF practices for custom controls, so there is not much room for improvement here, data templating is not supported, the MdiContainer is a UserControl and there are very limited dependency properties.
If you really want to continue working with this control with your current approach, you could:
Create a custom attached behavior to synchronize your view model collection with the Children collection of the MdiContainer and vice-versa, see XAML behaviors in WPF.
Use the Loaded event to assign the Children collection to your view model property.
<mdi:MdiContainer Name="Container" Grid.Row="1" Background="GhostWhite" Loaded="MdiContainer_OnLoaded">
private void MdiContainer_OnLoaded(object sender, RoutedEventArgs e)
{
var mdiContainer = (MdiContainer)sender;
var dataContext = (Main)mdiContainer.DataContext;
if (dataContext == null)
return;
dataContext.Children = mdiContainer.Children;
}
Use an EventTrigger on the Loaded event with a custom trigger action that sets the Children collection. This is just a different variant of the previous approach that does not require code-behind.
The new XAML behaviors for WPF package, which replaces the legacy Blend behaviors from the System.Windows.Interactivity namespace already includes such a trigger action. Install the Microsoft.Xaml.Behaviors.Wpf NuGet package and use this:
<mdi:MdiContainer Name="Container" Grid.Row="1" Background="GhostWhite">
<behaviors:Interaction.Triggers>
<behaviors:EventTrigger EventName="Loaded">
<behaviors:ChangePropertyAction TargetObject="{Binding DataContext, ElementName=Container}"
PropertyName="Children"
Value="{Binding Children, ElementName=Container}"/>
</behaviors:EventTrigger>
</behaviors:Interaction.Triggers>
</mdi:MdiContainer>
Note that with these approaches, you either synchronize to your own collection or you work directly with the collection of the MdiContainer that you passed to your view model. These are only workarounds. If you would want to implement this in a clean and MVVM compliant way, I think you would need to extend or fix the control itself, which is rather costly and not recommendable, since it seems to be dead anyway.
I know you can create custom controls and dependency property for wpf controls like expained here http://msdn.microsoft.com/en-us/library/ms753358.aspx, I want to know if you can create custom dependency property in the same way for devExpress Controls ? and how ?
There is no way to bind multiple items in comboxBoxEdit control. I want to create a dependency property called SelectedItems on ComboBoxEdit.
I already created a custom property on normal ComboBox called SelectedEnumeration which binds directy to the enums and gets the value. No need to use ObjectDataProvider.
There is no way to bind multiple items in comboxBoxEdit control.
Wrong. Check DevExpress.Xpf.Editors.CheckedComboBoxStyleSettings
Basically, you can bind ComboBoxEdit.EditValue to a collection, which gets populated with the selected items.
<dxe:ComboBoxEdit ItemsSource="{Binding MyItems}"
EditValue="{Binding SelectedItems}">
<dxe:ComboBoxEdit.StyleSettings>
<dxe:CheckedComboBoxStyleSettings />
</dxe:ComboBoxEdit.StyleSettings>
</dxe:ComboBoxEdit>
ViewModel:
public class SomeViewModel
{
public ObservableCollection<MyClass> MyItems {get;set;}
public ObservableCollection<MyClass> SelectedItems {get;set;}
}
I already created a custom property on normal ComboBox called
SelectedEnumeration which binds directy to the enums and gets the
value. No need to use ObjectDataProvider.
You're putting too much responsibility on the UI, where it does not belong. Create a proper ViewModel and have your data processed by the ViewModel in such a way that it facilitates regular DataBinding to the UI. Don't resort to reflection and other types of uneeded hacks in order to put logic in the wrong layer.
Introduction
I have an application that imports lab instrument data while it is running. This data is imported and then displayed in a ListView at an interval set by the end-user as per his or her testing requirements. When a value of interest appears in this ListView that they watch, they then press a Start button and the application begins performing calculations on that datum and subsequent data until a Stop button is pressed. So on the left side of the screen is a View for displaying the imported data and on the right side is another View for watching the values and statistics as they are calculated and displayed.
The Current Code
The View that displays the ListView where data is imported to is the ImportProcessView.xaml and it sets its DataContext to the ImportProcessViewModel.cs. The VM I've just introduced has a property ObservableCollection<IrData> that the ListView, I've also just described, binds to. Now to the interesting part...
The ImportProcessView has a ContentControl that sets it's content dynamically a UserControl representing the controls and fields specific to the type of Phase that is chosen by the end-user.
<StackPanel Background="White" Margin="5">
<ContentControl Content="{Binding CurrentPhaseView}"/>
</StackPanel>
There are three PhaseViews, each in its own User Control and each sets it's DataContext to the ImportProcessViewModel. As a result I am getting some severe VM bloat to the tune of 2000 lines. Ridiculous. I know. The reason for the bloat is because the ImporProcessViewModel is maintaining state through properties for each of the three PhaseViews and not only that but contains methods for performing calculations whose data is stored and displayed in these "PhaseViews".
What I am trying to achieve
Obviously before the ImportProcessViewModel becomes more unwieldy, I need to break it up so that each PhaseView has its own ViewModel, but also such that each ViewModel maintains a relationship back to the ImportProcessViewModel for sake of the dependency imposed by the ObservableCollection of IrData.
R&D
I've done my research on ViewModels communicating with each other, but most of the results involve applications that were written with a specific MVVM framework. I am not using a framework, and at this point in the project it would be too late to refactor it to start using one.
I did, however, find this article and the answer offered by 'hbarck' suggests something simple like composition to achieve the result I want, but since I don't have much experience with DataTemplates I don't understand what is meant when he/she suggests exposing "the UserControl's ViewModel as a property on the main ViewModel, and bind a ContentControl to this property, which would then instantiate the View (i.e. the UserControl) through a DataTemplate"
Specifically, I don't understand what is meant by "bind a ContentControl to this property which would then instantiate the View through a DataTemplate".
Can someone clarify by way of an code example what is meant by instantiating a view through a DataTemplate in the context of this example?
Additionally, is this a good approach (as suggested by 'hbarck')?
As one can see, I am already setting the Content property of a ContentControl to the Phase View that is to be instantiated. I just don't know know what involving a DataTemplate would look like.
I don't understand what is meant when he/she suggests exposing "the
UserControl's ViewModel as a property on the main ViewModel, and bind
a ContentControl to this property, which would then instantiate the
View (i.e. the UserControl) through a DataTemplate"
A DataTemplate allows you to specify a relationship between a view (such as a user control) and a view model.
<DataTemplate DataType="{x:Type myApp:MyViewModel}">
<myApp:MyUserControl />
</DataTemplate>
This tells a ContentPresenter to display MyUserControl whenever its content property is set to an instance of MyViewModel. The view model will be used as the user controls DataContext. Typically, the DataTemplate is added to your application resources.
What the author of that answer is saying is that you could have a viewModel that has a property of another viewModel type which is bound to the Content property of the ContentPresenter.
<ContentPresenter Content="{Binding ParentViewModel.ChildViewModelProperty}"/>
Providing you have a DataTemplate that specifies a relationship between your ChildViewModel and your user control, WPF will automatically load the user control into your view.
This answer I provided to another question might also provide you with some help.
I need to break it up so that each PhaseView has its own ViewModel,
but also such that each ViewModel maintains a relationship back to the
ImportProcessViewModel.
This will allow you to break your viewModels into smaller, more manageable viewModels that look after themselves. This will leave you with the problem of communicating between the viewModels.
If you nest your viewModels as suggested, then your child viewModels could expose events that the parent viewModel can bind to so it is notified when something changes. Something like this:
public class ParentViewModel // Derive from some viewModel base that implements INPC
{
public ParentViewModel()
{
childViewModel = new ChildViewModel();
childViewModel.SomeEvent += someEventHandler;
// Don't forget to un-subscribe from the event at some point...
}
private void SomeEventHandler(object sender, MyArgs args)
{
// Update your calculations from here...
}
}
This is simple and doesn't require any additional frameworks. Some might argue against this method but it is a valid solution that works. The downside is that the viewModels have to know about each others existence in order to subscribe to the events so can end up being tightly-coupled. You can use standard object-oriented design principles to get around this though (I.E. derive your child viewModel from an interface so that the parent only knows about the interface and not the implementation).
If you really want to go for loosely-coupled communication then you need to use some sort of event aggregation or message bus system. This is similar to the above method except there is an object that sits between the view models and acts as a mediator so that the viewModels do not have to know of each others existence. My answer here provides some more information.
There are pre-existing solutions available but this would involve taking on an additional framework. I would advise using Josh Smiths MVVM foundation as it is very simple and you would only need to use a single class anyway.
While Benjamin's answer is really elaborate and very helpful, I'd like to clarify how what I wrote in the other post would apply to your problem:
You'd have three different PhaseViewModel-Classes for your different phases, probably derived from one common base class, let's say PhaseVMBase.
Instead of a CurrentPhaseView property, you'd probably have a CurrentPhaseVM property. This would be of type Object or PhaseVMBase, and return one of the three PhaseViewModel classes, depending on what the user chose in the main ViewModel.
PhaseVMBase would have an UpdateData method, which would be called by the main ViewModel whenever it received new data that should be processed by the phase view. The main ViewModel would call this method on whatever happened to be the CurrentPhaseVM at the moment. The PhaseViewModels would implement INotifyPropertyChanged, so that changes as a result of UpdateData would be visible to bound controls.
Your DataTemplates would be declared in the resources of the main view, e.g. the main window,
like this:
<DataTemplate DataType="{x:Type my:Phase1VM}">
<my:Phase1View/>
</DataTemplate>
<DataTemplate DataType="{x:Type my:Phase2VM}">
<my:Phase2View/>
</DataTemplate>
<DataTemplate DataType="{x:Type my:Phase3VM}">
<my:Phase3View/>
</DataTemplate>
Notice that there is no x:Key, only the DataType value. If declared like this, WPF would choose the appropriate DataTemplate when asked to display an object of type Phase1VM, Phase2VM or Phase3VM, respectively. Phase1View, Phase2View and Phase3View would be UserControls which would know how to display the different ViewModels. They wouldn't instantiate their ViewModels themselves, but expect that their DataContext is set to an instance of their respective ViewModel from outside.
Under the assumption that the ContentControl which should show the phase view is declared in the main view, and that the DataContext there would be the main ViewModel, you'd declare the ContentControl like this:
<ContentControl Content="{Binding CurrentPhaseVM}"/>
Depending on the actual type of CurrentPhaseVM, this will choose one of the three DataTemplates, and display the appropriate UserControl. The UserControl's DataContext would automatically be the ContentControl's Content, since that would the object which caused the DataTemplate to be chosen.
EDIT: Lists and code formatting don't go together, it seems...
I may be making this more complicated than necessary ... but here goes.
I have my MainPage.xaml, in there I have references to two other UserControl's, ResultsView, and DetailsView.
Inside of the ResultsView, I have a ListBox bound to an ObservableCollection of custom items, I have a DataTemplate that is rendering each item. The item has a CaseID, and when I click on it, it's displayed as a HyperlinkButton, I need a Command I've set in the MainPageViewModel to fire, which handles changing the visibility to hide the ResultsView, and show the DetailsView.
How do I bind the Command of the Hyperlinkbutton to the Command located in my MainPageViewModel?
Thanks in advance!
edit for clarification:
MainPage.xaml
<resultsView:ResultsView/>
<detailsView:DetailsView/>
Each of the two views (ResultsView & DetailsView) have their own ViewModel. So I'm going from my DataTemplate which resides in a ListBox inside my ResultsView, I need to go up to the MainPageViewModel, an extra step than your answer mentioned. Tried your method naming my MainPage.xaml to Name="mainPage", and using that as the ElementName in my HyperlinkButton, but no luck. I'll do some research on the RelativeSource option and see if I can make that work.
Thanks for your help so far.
edit 2: Forgot to add that the DataTemplate is in a ResourceDictionary, not in the ResultsView.
Well, it depends on the specific details, not all of which you've told us, but I can give you some examples.
Let's say that your DataTemplate for the custom items resides in the XAML of the ResultsView user control. That's a good spot for it but you might have put it in a resource dictionary.
As you say, the ListBox is bound to the collection of custom items. Let's further assume that the custom items collection is itself a property in your MainPageViewModel. And you've said clearly that the command you want to bind to is also in MainPageViewModel.
So the problem, which is a very common one, is that you are working inside a template associated with a binding to a collection, and so your DataContext is a custom item. It is no longer the main view-model itself. That's great as you show appropriate properties of the custom item like CaseID. But it's not great when you want to escape to the top-level of view-model.
If what I've said is true, then the ResultsView user control is in fact probably bound to the MainPageViewModel because you haven't "drilled into" the custom items collection yet. So, what you need to do is find a way using the binding syntax to reference the ResultsView user control from inside the DataTemplate for the ListBox. If you can do that, then you've escaped the collection.
There are two main approaches to do this:
ElementName syntax
RelativeSource syntax
I'll describe ElementName syntax and you can look up the other one.
Part 1) Name your ResultsView UserControl element like this:
<UserControl ....
Name="resultsView">
<!-- ... -->
Part 2) Inside your DataTemplate where you are defining the appearance of the hyperlink use the ElementName syntax to refer to that element:
<TextBlock>
<Hyperlink Command="{Binding DataContext.ItemDetailsCommand, ElementName=resultsView}"/>
</TextBlock>
So first we use ElementName to get the ResultsView UserControl element, and then we have a path with two pieces: the first piece is the DataContext property of the ResultsView which gives us the MainPageViewModel (yeah!), and then the property of the command we want to invoke.
That's one way to "escape" the binding and issue commands found at a higher level in the view-model.
I have a ListBox bound to an observable collection of DiceViewModel. Whenever I click a button to add a new item, the ListBox displays the new item like I expect. Everything so far is working well.
<ListBox
ItemsSource="{Binding Path=AllDice}"
DisplayMemberPath="Value"/>
However, I have another button to roll all existing dice. The items already listed in the box don't get updated, and I'm not sure how to enforce this while keeping to the MVVM design pattern.
Also, my DiceViewModel already implements INotifyPropertyChanged.
Any suggestions?
After some more digging around, here's what I've found. The ObservableCollection doesn't automatically register itself with my DiceViewModel's INotifyPropertyChanged event. So any property changes don't get handled.
However, there is a way to do it in the xaml file:
I added this namespace definition to my Window element.
xmlns:vm="clr-namespace:Rolling.ViewModel"
Then I modified my ListBox to use a DataTemplate with a specified DataType:
<ListBox ItemsSource="{Binding Path=AllDice}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type vm:DiceViewModel}">
<TextBlock Text="{Binding Path=Value}"/>
</DataTemplate>
</ListBox.Resources>
</ListBox>
With the specified DataType, the ObservableCollection could register itself with my collection items, receive their events, and then fire it's own CollectionChanged event.
I hope this helps some other people with this poorly documented feature.
You need to implement the INotifyCollectionChanged interface on the collection that the items are bound to and then have it fire the CollectionChanged event to indicate that the collection changed.
This will cause a refresh of the entire list.
In the case of ObservableCollection INotifyPropertyChanged will only notify on changes to the structure of the collection, generally this is the addition and removal of items. The collection has no knowledge of changes to the properties of an individual item within the collection. Instead that individual item itself is responsible for sending notification of its properties changing.
The reasoning behind this goes back to class responsibility and separation of concerns. Since the DiceViewModel likely has data related rolling a die and the value of its last roll then it would follow that it would send notification when its own properties change.