WPF: MVVM Create custom dependency property for devExpress Controls - c#

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.

Related

How to bind mdiContainer children to viewModel property?

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.

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?

Binding properties of multiple tree views to the same ViewModel

I am working on a MVVM implementation, where i'll spawn multiple views (side by side) each containing a tree control.
each of the views will have a similar tree, with a copy of [almost] all the same items.
I would like to synchronize the IsExpanded property on all the view/TreeView's..
meaning, if i collapse one node, i would like all of them to collapse (and some goes for column widths etc).
One way to do this, would be to bind all views to the same viewmodel, and have a DependencyProperty on that ViewModel, and set up the binding as Two Way on each view. However, i need each view to be bound to a separate viewmodel so that it can display unique values. I just need to synchronize a few properties of the tree, such as IsExpanded and Width.
What would be the best approach here?
You can use Prism and EventAggregator service from it to exchange data between view models.
There's no reason you can't have different collections within a single ViewModel, if that is the best design option. Especially if your multiple Trees / Collections are filtered from some 'complete set'; it might actually make more sense.
Just add multiple collections to your ViewModel, and bind to them.
public class MyViewModel : INotifyPropertyChanged
{
public ObservableCollection<MyItem> FirstTreeCollection
{
get
{
// whatever you need to do here
}
}
public ObservableCollection<MyItem> SecondTreeCollection
{
get { /* etc */ }
set { /* etc */ }
}
// etc
public bool Collapsed
{
get;
set;
}
}
and your Views should bind accordingly
// in your first view that contains a tree
<UserControl x:Class="View1" ...>
<TreeView Name="FirstTree"
ItemsSource={Binding FirstTreeCollection}
Collapsed={Binding Collapsed} ... >
// & in your second view that contains a tree
<UserControl x:Class="View2" ...>
<TreeView Name="SecondTree"
ItemsSource={Binding SecondTreeCollection}
Collapsed={Binding Collapsed} ... >
To clarify, I'm suggesting that you use a single ViewModel for all of these Tree-containing Views.
The ViewModel won't need a DependencyPropery--it will just need to expose a property that implements INotifyPropertyChanged.
The two ViewModels will need to have some way of sharing state, and exposing a property that represents that state. How you share the state depends heavily on how your ViewModels are instantiated (and probably other factors). For example, if your two VMs are being instantiated by some parent object, the parent may create one instance and pass it to both VMs in their constructors.
If you display the treeview's using xaml, you can bind every treeview to the first treeview spawned.
For example you can use some binding like this:
<TreeView Name="FirstTreeView" />
<TreeView Name="SecondTree"
IsExpended = {Binding Path=IsExpanded, ElementName=FirstTreeView, Mode=TwoWay}/>

Is it possible to add a default ComboBox entry with command binding in XAML?

I'm working on my first true WPF MVVM pattern application.
Currently I have a number of ComboBoxes on various screens that are bound to Collection classes and properties of the relevant ViewModel class.
They always have an entry with the text <Add>, which is really an empty object class and I currently use it to trigger an AddNewObject event if the Property bound to the SelectedItem has <Add> in its ToString() output. This strikes me as cumbersome and it ties the View too closely to the View model for my liking.
e.g.
<ComboBox ItemsSource="{Binding AllObjects}" SelectedItem="{Binding SelectedObject}" />
then in ViewModel code:
public SomeObjectType SelectedObject
{
get{return this.fieldSomeObjectType;}
set
{
if(null==value)
return;
if(value.ToString().Contains(#"<Add>"))
{
if(null!=this.AddNewObject)
{
this.AddNewObject;
}
}
}
}
Is there a way in XAML of adding this extra line into the ComboBox drop down list and binding it to an AddNewObject Command?
The code you've written in your view has nothing to do with your business logic. Its fine.
MVVM doesn't say that you shouldn't have anything in the codebehind of the view. Showing 'Add' is a requirement on the view and can be handled by the code behind of view.
In ASP.NET I've been doing this that I databinded the list control to some data but also specified some items in the html and it would merge them. Have you tried that?
use CompositeCollection for merging a default item with a itemsource. http://msdn.microsoft.com/en-us/library/ms742405.aspx

WPF Databinding in XAML

I have little problem with databinding in my current project.
I have an ObservableCollection I want to bind to an ListBox.
public ObservableCollection<GeoDataContainer> geoList = new ObservableCollection<GeoDataContainer>();
...later...
geoListBox.ItemsSource = geoList;
This code works fine. The Listbox has a datatemplate and everything looks perfect.
But I don't want to use C# code for binding. I want to make the binding in the XAML Code.
I am searching for days but I don't get it. These are two lines C# code but to archive this in XAML it seems impossible without creating my own class for my collection or adding a DataProvider or resources or whatever.
Is there no easy way to do it?
All you have to do is expose the collection and bind to it. For example, if you expose it as:
public ICollection<GeoDataContainer> GeoList
{
get { return geoList; }
}
You will be able to bind to it as:
<ListBox ItemsSource="{Binding GeoList}"/>
The "trick" is to make sure that the DataContext of the ListBox is the class that exposes the GeoList property.
Another good way would be instantiating geoList as a resource
<WindowResources>
<l:GeoCollection x:Key="geoList"/>
</WindowResources>
Then you have
GeoCollection geoList = FindResource("geoList") as GeoCollection;
Of course, this is for cases when the data is related to the view only. If this is related to model or modelview, you use DataContext and bind to its properties.
Kent suggestion is the way to go...
On a further note, if you do not wish to set your DataContext to the list, you can also retrieve the property with an another form of binding:
Make sure your root control has a name, i.e. "Root"
{Binding ElementName=Root, Path=GeoList}

Categories

Resources