How to bind mdiContainer children to viewModel property? - c#

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.

Related

Custom XAML Binding SOURCE Class

I am trying to find a better way to declare the ViewModel a UWP XAML Page uses.
At this moment,
I declare a ViewModel class ViewModelClass that contains my data properties.
Then I declare an instance of that class as StaticResource of the Page. I like calling those VIEW for consistency across all my Page designs.
Finally, I declare the Page's DataContext as a Binding to the StaticResource VIEW.
This yields a page that understands what data structure is in use and allows AutoComplete when working Bindings. Nice, though lots of lines of same-old-same-old code.
Only, it is not really suitable to ViewModels as the declared resource is a static resource. It is instantiated when the page is instantiated. Most pages will receive a ViewModel parameter upon NavigatedTo, which cannot be used to replace the static resource, because it is, well. static.
So I end up changing the Page's DataContext upon navigation from the initial reference to VIEW to the ViewModel instance I actually want to use.
Big caveat is to declare the back-reference to the page's DataContext when deep in the bowels of a Master-Detail situation is rather horrible. Imagine a collection whose display is in part depending on a Master's property.
How do you tie back robustly to the DataContext of the page from anywhere inside the page?
I have tried giving the page a Name (PAGE for simplicity) and then using ElementName=PAGE, Path=DataContext.someProperty. Ugly, plus you lose all information of the class represented by DataContext.
Another approach is to create a Wrapper around the actual ViewModel called StaticViewModel that has only one property: public ViewModel viewModel. Now I can declare the wrapper as a StaticResource, and tell the page's top-level FrameworkElement to use VIEW.viewModel as its DataContext. Works, and reliably, but sooooo ackward and cumbersome.
I would LOVE to implement a SOURCE class for bindings called PageDataContext that would do nothing else but to loop into the page and get the DataContext from there.
Imagine: {Binding someProperty, Source={PageDataContext}
How would I go about declaring said Source class for a UWP app???
I would LOVE to implement a SOURCE class for bindings called PageDataContext that would do nothing else but to loop into the page and get the DataContext from there. Imagine: {Binding someProperty, Source={PageDataContext}
For your requriment, you could implement your viewmodel in the page Resources and give it x:key. When you bind the property of viewmodel you could access this viewmodel with x:key Source={StaticResource ViewModel} for more please refer the following code.
ViewModel
public class ViewModel
{
public ViewModel()
{
Visibility = false;
}
public bool Visibility { get; set; }
}
Xaml
<Page.Resources>
<local:ViewModel x:Key="ViewModel" />
</Page.Resources>
<TextBlock
Width="100"
Height="44"
Text="{x:Bind Name}"
Visibility="{Binding Visibility, Source={StaticResource ViewModel}}" />
Well, no dice so far.
Working with all those options I found that the best one is a Wrapper class that can be assigned to a static resource which in turn is the basis for the page's DataContext. The wrapped instance of the actual ViewModel is then assigned during OnNavigatedTo() and used as the DataContext of the basic FrameworkElement of the page.
Brr, so much verbose code.
STILL WONDERING how to implement a different version of a source directive. Is there no way to declare one's own Source for a Data Binding? Is this stuff really hard-coded into the framework???

Multiple sources bound to same target dependency property

Is it possible to attach multiple (two-way) source bindings to a dependency property? That is, so that if one source changes, the DP gets updated via one binding, and the change would then get propagated to the second source via the second binding.
In my scenario, the dependency property is in a user control, the first binding is to its internal view-model, and the second binding is for the view-model of the consumer of the user control.
Below is for illustration. The consumer of the user control looks like this:
<MyControl SelectedValue="{Binding Selected,Mode=TwoWay}" />
Now "MyControl" has the "SelectedValue" defined as a dependency property. The XAML for the control binds to its dependency property like this:
<UserControl>
<Grid x:Name="LayoutRoot">
<TextBox Text="{Binding SelectedValue,Mode=TwoWay,
RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
</Grid>
</UserControl>
"MyControl" has its internal data context set, in the control's constructor, to its own view model:
LayoutRoot.DataContext = new ViewModelForControl();
So far so good, but if I then attempt to add the second binding, that being the dependency property to a "SelectedInternal" property on the internal view-model --
SetBinding(SelectedValueProperty, new Binding("SelectedInternal") {
Source = LayoutRoot.DataContext,
Mode = BindingMode.TwoWay
});
-- then the first binding is destroyed. Is there a way to add this second binding while preserving the first?
Is this being overthought?
Why not simply do the plumbing in the code behind of the custom control and forgo binding?
This can be done by
SelectedValue dependency property will utilize its changed handler and upon any change set SelectedInternal to the new value.
When SelectedInternal changes write to the property SelectedValue.
You create the VM on the control, so you have access to the VM and its property, which can provide the vectoring of the data for two way transfer.
At the end of the day binding is just getting a reference via reflection. In this case how one gets a reference is immaterial to simply writing back and forth between two properties.
Or am I missing something?

Expose the properties of a usercontrol which is built in MVVM

There are lots of questions like this one, but still can't get what I really want, and all of them have something that differs of mine and that is:
I have a UserControl:
Built separately in a class library project called UCProject;
The project UCProject has a View for the control, and its ViewModel;
The UserControl binds some of its own controls and UIElements properties in the View to properties declared in the ViewModel in, of course, the UCProject;
How can I show or expose or make the ViewModel Properties accessible to the page or the window of the project (that may be called for instance GlobalProject) that may host this UserControl;
I'am building this UserControl, I want to build events, properties to it ... and I want it to be used by others as a given assembly to them, so its code is not accessible, they only can consume it, I want to respect the MVVM pattern, and I don't have a clear idea how to realize that, should I write this properties and events in the CodeBehind of the UserControl View or should I put them in the ViewModel, and in that case how can I access them from outside, just like we daily use third-party controls
If i assume it right, you want to expose properties of child UserControl's ViewModel to containing parent root element which might be window or other UserControl.
There are two approaches to it:
First, DataContext DP is inheritable i.e. child controls inherit it from parent control unless set it explicitly to some other value. So, what you can do is have common ViewModel and set it as DataContext on parent UserControl and both have access to its properties.
Second, in case you want separate ViewModels for parent and child UserControls. You can always access properties of child's ViewModel via DataContext. Let me explain with an example:
<UserControl x:Name="ParentUserControl">
<StackPanel>
<local:ChildUserControl x:Name="Child"/>
<TextBlock Text="{Binding DataContext.PropertyName, ElementName=Child}"/>
</StackPanel>
</UserControl>
where PropertyName is property in ViewModel of ChildUserControl.
Here as you can see TextBlock which lies in ParentUserControl is binding to property in ViewModel of child UserControl.
You haven't specifically declared which parts of these projects you have control over so I'll assume you can changed all of them. Basically you need to use dependency properties in the User Control. First you bind the view to the ViewModel, when you add the User Control to the view it will effectively inherit the DataContext and you can bind the dependency properties you've created to the various parts of the ViewModel. In the User Control itself you bind the various FrameworkElements to the dependency properties in the User Control, not to the ViewModel itself.
Make sense?
I had to implement those Properties in the CodeBehind using DependencyProperties that's the best way to do it

WPF: MVVM Create custom dependency property for devExpress Controls

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.

ViewModels that talk to each other without a Framework

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...

Categories

Resources