Binding with multiple view models - wpf

I have a viewmodel for a usercontrol that is defined in a data template for the viewmodel. I would like to bind the 'GridViewData' property of the usercontrol to the 'Data' property of the viewmodel.
I am still new to WPF and terrible at bindings, so please be kind :p
XAML:
<Window x:Class="ReportUtility.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lite="clr-namespace:ReportUtility.Controls.LiteGrid"
xmlns:vm="clr-namespace:ReportUtility.ViewModels"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
Title="MainWindow" Height="350" Width="525">
<!--This is the view model I want to bind to variable name is Grid: hosted in the content control below-->
<Window.Resources>
<DataTemplate DataType="{x:Type vm:LiteGridViewModel}">
<lite:LiteGrid GridViewData="{Binding ??}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Content="TestButton" HorizontalAlignment="Stretch" Command="{Binding ExecuteQueryCommand}"/>
<TextBox Grid.Row="1" Text="{Binding Path=SqlCommandText, UpdateSourceTrigger=PropertyChanged}"/>
<ContentControl Content="{Binding Grid}" Grid.Row="2" Background="Red"/>
<Border Background="Aqua" Grid.Column="1" Grid.RowSpan="3"/>
<GridSplitter Grid.Column="0" Width="3" Grid.RowSpan="3"/>
</Grid>

You would bind to Data
<DataTemplate DataType="{x:Type vm:LiteGridViewModel}">
<lite:LiteGrid GridViewData="{Binding Data}"/>
</DataTemplate>
It would even be better to do this binding in the LiteGrid UserControl instead of relying on the XAML that uses the control to set the value.
<UserControl GridViewData="{Binding Data}">
...
</UserControl>
Your bindings always reference the current object's DataContext. Since your DataTemplate is for type LiteGridViewModel, the DataContext within that DataTemplate is always going to be of type LiteGridViewModel.
For example, if you have a class like
public class MyClassA
{
MyClassB ClassB {get; set;}
}
public class MyClassB
{
MyClassC ClassC {get; set;}
}
And a ViewModel property of MyClassA ClassA (meaning you can reference ClassA.ClassB.ClassC), you could do something like
<ContentControl Content="{Binding ClassA}">
<ContentControl Content="{Binding ClassB}"> <!-- DataContext is MyClassA -->
<ContentControl Content="{Binding ClassC}"> <!-- DataContext is MyClassB -->
<!-- DataContext is MyClassC -->
</ContentControl>
</ContentControl>
</ContentControl>

Related

How can I set the binding for a control's property (which is inside DataTemplate and UserControl) to use the ItemSource's given property?

I would like to make a UserControl which have a DataTemplate, and inside that DataTemplate there are controls. I would like to bind to those nested (inside the DataTemplate) controls' properties so I can set them when I reuse this UserControl. The nested controls will use the ItemSource's properties but the property names of the ItemSource's properties could be different.
The UserControl:
<UserControl x:Class="ContextMenu.BaseFilterUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
x:Name="Self">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="70" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Margin="10"
Text="Owners" />
<Button Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="10"
Click="FilteButtonClicked"
Width="40"
Height="40"
x:Name="FilterButton">
<Popup x:Name="FilterBoxPopup"
PlacementTarget="{Binding ElementName=FilterButton}"
Placement="Bottom"
StaysOpen="False">
<Border BorderBrush="Black"
Background="White"
Margin="2">
<ListView ItemsSource="{Binding ElementName=Self, Path=FilterList}"
x:Name="FilterListView"
Height="300"
Width="150">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!--<CheckBox IsChecked="{Binding IsChecked}" />-->
<!--<TextBlock Text="{Binding Name}" />-->
<!--This is where I don't know how to properly bind eg. the above control, things I tried:-->
<!--<TextBlock Text="{Binding ElementName=FilterListView, Path=FilterElementName}" />-->
<!--<TextBlock Text="{Binding ElementName=Self, Path=DataContext.FilterElementName}" />-->
<!--<TextBlock Text="{Binding ElementName=FilterListView, Path=DataContext.FilterElementName}" />-->
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
</Popup>
</Button>
<TextBlock Grid.Column="3"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10"
Text="{Binding ElementName=Self, Path=SelectedNames}" />
</Grid>
</UserControl>
This how the UserControl is used, the FilterElementName="Name" is what I would like to set, depending on the list binded with FilterList list:
<local:BaseFilterUserControl FilterList="{Binding Owners}"
FilterElementName="Name"
SelectedNames="{Binding SelectedNames}"/>
In this case the Owners is a simple IReadOnlyList of an Owner class. The Owner class has a string Name property. But I will use this UserControl again with different list eg. where I would like to use the Versions list's Release property (for the TextBlock inside the UserControl):
<local:BaseFilterUserControl FilterList="{Binding Versions}"
FilterElementName="Release"
SelectedNames="{Binding SelectedReleases}"/>
The ListView is properly populated with items, so the FilterList DependencyProperty is working. But the nested controls are only working when I hard code the bindings:
<TextBlock Text="{Binding Name}" />
For this to work you would need to bind the Path-property of your TextBlocks Text-Binding to the FilterElementName property of your UserControl. Unfortunately the Path property of the Binding class is not a DependencyProperty and therefore not bindable.
One way to to achieve your goal would be to use the DisplayMemberPath property of the ListView, which is bindable:
<ListView x:Name="FilterListView"
Width="150"
Height="300"
ItemsSource="{Binding ElementName=Self, Path=FilterList}"
DisplayMemberPath="{Binding ElementName=self, Path=FilterElementName}"/>
If this approach does not work because you need to specify a more complex ItemTemplate, another way would be create a property of type DataTemplate in your UserControl, use that as ItemTemplate in the ListView and specify it from outside like so:
<local:BaseFilterUserControl FilterList="{Binding Versions}"
SelectedNames="{Binding SelectedReleases}">
<local:BaseFilterUserControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Release}" />
</DataTemplate>
</local:BaseFilterUserControl.ItemTemplate>
</local:BaseFilterUserControl>

Binding the combobox values for an empty datatemplate of a bound itemscontrol wpf

I have an itemscontrol that is bound to a datasource via LINQ. I am trying to create a repeatable control that a) displays the records returned from the LINQ query and b) allows the user to add a new row.
I have 2 collections:
a) fareItemCollection this contains a collection of fareItem objects which include a fareDate and PermitNumber.
b) permitNumbers this is an Ienumerable
I can display the fare items in a repeatable itemsControl. I want to be able to show the permit number as a drop down list (i.e. combobox) of permitNumbers with the permitNumber for that fare selected. The user should be able to select a different permit number to assign to that fareItem if they wish.
I have no problem with retrieving the required data via LINQ, it is how I bind the permitNumbers data in the combobox that I am struggling with. I understand how to bind a dataset to a combobox normally but not when it is within an items control bound to another source. Is this even possible or is there another way to approach this?
Here's my xaml so far - Note PermitNumbers is the name of my iEnumerable which is a public property within the window's class:
<ItemsControl Name="fareItemsControl" ItemsSource="{Binding}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width="160" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock>Date</TextBlock>
</StackPanel>
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock>Driver</TextBlock>
</StackPanel>
<StackPanel Grid.Column="0" Grid.Row="1">
<TextBlock Text="{Binding FareDate, StringFormat={}\{0:dd/MM/yy\}, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource FormFieldTextBoxStyle}">
</TextBlock>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="1">
<ComboBox ItemsSource="{Binding Path=PermitNumbers, UpdateSourceTrigger=PropertyChanged}" IsEditable="True"></ComboBox>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Style>
<Style TargetType="ItemsControl">
<Style.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width="160" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock>Date</TextBlock>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="0">
<TextBlock>Driver</TextBlock>
</StackPanel>
<StackPanel Grid.Column="0" Grid.Row="1">
<DatePicker SelectedDate="{Binding FareDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource datePickerStyle}"> </DatePicker>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="1">
<ComboBox ItemsSource="{Binding Path=PermitNumbers, UpdateSourceTrigger=PropertyChanged}" IsEditable="True"></ComboBox>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ItemsControl.Style>
</ItemsControl>
Thanks
Kay
Further information:
The following is the fareObject that I am using within my fareObjectsCollection that is bound to the fareItemsControl above:
public class FareProxy
{
DateTime _fareDate;
public DateTime FareDate
{
get
{
return _fareDate;
}
set
{
_fareDate = value;
}
}
IEnumerable<string> _permitNumbers;
public IEnumerable<string> PermitNumbers
{
get
{
return _permitNumbers;
}
set
{
_permitNumbers = value;
}
}
}
For each fare item the date displays correctly within the items control but the permitNumbers won't bind to the permitNumbers combobox. I tried to create a permitNumbers property within the class of the window I am using and load the permitNumbers by setting the source path of the comboBox to the permitNumbers property as suggested in the post below but this didn't work either.
You need to make a few changes. First things first... whichever Window or UserControl that your fareItemsControl ItemsControl is in has an object set as its DataContext... I think that you've just set the fareItemCollection as the DataContext. That's your first mistake.
That collection property is in some class. Move your permitNumbers collection as a property to that same class. Now set an instance of that class (that contains both collection properties) as the DataContext.
Now, you'll need to update your ItemsControl.ItemsSource property:
<ItemsControl Name="fareItemsControl" ItemsSource="{Binding fareItemCollection}" ... />
For the final part, #ethicallogics was correct to point out that you'd need to use a RelativeSource Binding to access your permitNumbers collection from inside a DataTemplate. However, he omitted the essential DataContext part. So the correct RelativeSource Path should be something more like this:
<ComboBox ItemsSource="{Binding DataContext.permitNumbers, RelativeSource={
RelativeSource AncestorType={x:Type Window}}}" IsEditable="True"></ComboBox>
This means that the ComboBox.ItemsSource is looking for a property named permitNumbers in the object that is set as the DataContext for the parent Window. That should do the trick.

Why is my WPF ContextMenu Item disabled?

I have a small but annoying problem with my ContextMenu in a WPF application.
I use a DataTemplate for my List of ViewModel-objects to create the visual elements. In the DataTemplate I simply set my UserControl like this:
<!-- Define a data-template for the 'RoomViewModel' class. This generates the UI for each node. -->
<DataTemplate DataType="{x:Type model:RoomViewModel}">
<network:RoomView network:ConstraintView.ArgumentChanged="ConstraintView_ArgumentChanged"></network:RoomView>
</DataTemplate>
I want the RoomView to have a ContextMenu where a RelayCommand should be executed if the user clicks on the MenuItem. My UserControl for the RoomView looks like this at the moment:
<UserControl x:Name="userControl" x:Class="NetworkUI.RoomView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:network="clr-namespace:NetworkUI"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" DataContextChanged="userControl_DataContextChanged">
<UserControl.Resources>
<!-- UI commands -->
<RoutedCommand x:Key="Commands.NewConstraintCmd"></RoutedCommand>
</UserControl.Resources>
<UserControl.CommandBindings>
<CommandBinding x:Name="newConst" Command="{StaticResource Commands.NewConstraintCmd}" Executed="NewConstraint_Executed"></CommandBinding>
</UserControl.CommandBindings>
<Grid Width="Auto" Height="Auto" MinWidth="50" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Width="Auto" Height="20" MinWidth="50" >
<Rectangle Stroke="Black" Fill="White" RadiusX="4" RadiusY="4" SnapsToDevicePixels="True" ></Rectangle>
<Grid Margin="-4" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" MinWidth="10" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<network:EditableTextBlock Grid.Column="1" Grid.Row="1" x:Name="nameTextBox" Text="{Binding Name, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center" BorderBrush="{x:Null}" />
<network:ConnectorItem Grid.Row="1" Grid.Column="0" DataContext="{Binding Connectors[0]}" />
<network:ConnectorItem Grid.Row="1" Grid.Column="2" DataContext="{Binding Connectors[1]}" />
</Grid>
</Grid>
<StackPanel Grid.Row="1" x:Name="roomProperties" Width="Auto" HorizontalAlignment="Stretch" >
<ItemsControl ItemsSource="{Binding PropertyGroups}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<network:ConstraintCollectionView></network:ConstraintCollectionView>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
<UserControl.ContextMenu>
<ContextMenu x:Name="contextMenu">
<MenuItem Header="new Constraint" Command="{StaticResource Commands.NewConstraintCmd}"></MenuItem>
</ContextMenu>
</UserControl.ContextMenu>
</UserControl>
I am not really sure if my UserControl code is ok. In my CodeBehind file I define a
public ICommand NewConstraintCmd;
-Command that should be executed when the user clicks the Item. My Executed handler is simple and looks like this:
private void NewConstraint_Executed(object sender, ExecutedRoutedEventArgs e) {
ViewModel.AddConstraint();
}
The ViewModel is indeed a RoomViewModel and the NewConstraintCmd is set in the DataContextChanged - event (not displayed here).
My simple question is, why is my ContextMenu-Item always disabled? I mean the RelayCommand.CanExecute is never checked, is this the problem maybe?
I didn't find any solution for my problem yet.
Thank you for any help!
Thank you Rohit Vats! I changed my ICommand definition to this:
public static readonly ICommand NewConstraint = new RelayCommand((rvm) => AddConstraint(rvm));
Since this is a static method I needed the parameter (the DataContext of my RoomView).
My Method is defined like this now:
private static void AddConstraint(object rvm) {
if (rvm is RoomViewModel)
(rvm as RoomViewModel).AddConstraint();
}
So first the parameter is checked and if it is ok (a RoomViewModel object) I call the actual function on the object.
I have no idea why it works now, really :/.
My XAML code for the MenuItem now looks like this:
<MenuItem Header="new Constraint" Command="network:RoomView.NewConstraint" CommandParameter="{Binding}"></MenuItem>
As mentioned before I need the CommandParameter to call a function on it. Since the needed Object is my DataContext I just use the {Binding} to pass the object.
This really works fine now, I hope it will work with InputBindings as well in the future :)!
Thank you again for the help!

WPF ComboBox bind itemssource to different datacontext in MVVM

I have a user control that has its datacontext set to a view model called EmployeeList_VM. I then have a ContentControl within that user-control that has its datacontect set to a public property (EmployeeSelection) of the view model. The ContentControl's datacontext is the same as the selected item binding of a listbox within the same user control.
I want to have a combobox (of an observblecollection called EmployeeStatus) within the ContentControl bind its selecteditem to a property of the EmployeeSelection datacontext of the ContentControl. I want the itemssource of the combobox to bind to a public property EmployeeStatus of the "parent" viewmodel EmployeeList_VM.
I can get a list of the employees status to appear wihtin the combobox. I can not get it to bind to the idStatus property of the EmployeeSelection.
Here is What I have so far (some code removed for readability):
<UserControl x:Class="FTC.View.EmployeeListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FTC_Application"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight"
mc:Ignorable="d"
DataContext="{Binding EmployeeList_VM, Source={StaticResource Locator}}"
d:DesignHeight="900" d:DesignWidth="1000">
<Expander x:Name="DetailExpander" Grid.Column="2" Header="employee detail" Style="{DynamicResource ExpanderStyle_FTC}" IsExpanded="True" Padding="8,0,0,10" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" >
<Button Content="" Style="{DynamicResource ButtonSave}" Command="{Binding SaveCommand}" ToolTip="Save Changes" Margin="0,0,10,10"/>
<Button Content="" Style="{DynamicResource ButtonUndo}" Command="{Binding UndoCommand}" ToolTip="Undo Changes" Margin="0,0,10,10"/>
</StackPanel>
<ScrollViewer Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ContentControl DataContext="{Binding Path=EmployeeSelection, Mode=TwoWay}" >
<!-- FTC SETTINGS GRID CONTENT-->
<Grid Grid.ColumnSpan="6" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock x:Name="SettingsHeading" Text="FTC Settings" Style="{StaticResource FTC_DetailHeading}" Grid.ColumnSpan="5"/>
<TextBlock Text="Status" Style="{StaticResource FTC_DetailLabelSub}" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="1"/>
<ComboBox Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="2"
Margin="5,5,16,5" Height="37"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.EmployeeStatus}"
SelectedItem="{Binding idStatus, Mode=TwoWay}"
SelectedValuePath="idStatus"
DisplayMemberPath="chrCode" FontSize="18"/>
</Grid>
</ContentControl>
</ScrollViewer>
</Grid>
</Expander>
</UserControl>
I even tried the following change (I named the ContentControl DetailControl) for the selected item of the combobox:
SelectedItem="{Binding ElementName=DetailControl, Path=DataContext.idStatus}"
Can someone please help me get the selected item binding hooked up properly. The combobox displays all the right items, but they do not bind to the EmployeeSelection.idStatus.
Thanks in advance.
so here is what worked for me, hope it can help someone else
<ComboBox Name="cbStatus" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="2"
Style="{StaticResource FTC_DetailComboBox}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.EmployeeStatus}"
SelectedItem="{Binding ElementName=DetailControl, Path=DataContext.employee_status}"
SelectedValuePath="idStatus"
SelectedValue="{Binding idStatus, Mode=TwoWay, ValidatesOnDataErrors=True}"
DisplayMemberPath="chrCode"/>

MVVM + UserControl + UserControl + DependencyProperty

I have a user control which displays addresses housed inside of another page that is bound to a viewmodel. The viewmodel has a primitive User which has a collection of Address objects. The User control will reside on several pages so I would like to be able to bind it to the address list via a dependency property. While my current solution is working, something about it just doesn't feel right and I thought I'd ask for a second opinion. I have chopped out a lot of code for brevity's sake.
Basically the page binds to the dependency property in the usercontrols code behind which then updates the usercontrol's datagrid by setting it's itemsource. This seems to me to break the basic tenants of MVVM.
AddressListView control:
<UserControl x:Class="Insight.Controls.AddressListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:tk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
xmlns:command="clr-namespace:PrismFramework.Implementors.Commanding;assembly=PrismFramework"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="840">
<UserControl.Resources>
<command:ObservableCommand x:Name="EditAddressCommand" Value="{Binding EditAddressCmd}"/>
<command:ObservableCommand x:Name="DeleteAddressCommand" Value="{Binding DeleteAddressCmd}"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<sdk:DataGrid Name="dgAddresses"
Height="Auto"
Width="Auto"
AutoGenerateColumns="False"
HeadersVisibility="None" >
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn x:Name="dgcAddresses"
Width="*" >
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border x:Name="bdrAddress"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="Auto"
Width="Auto"
BorderBrush="Silver"
BorderThickness="1"
Padding="0"
Margin="1,1,1,1">
<Grid x:Name="grdAddressItem"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="Auto"
Width="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="17" MinHeight="17"/>
<RowDefinition Height="17" MinHeight="17"/>
<RowDefinition Height="17" MinHeight="17"/>
<RowDefinition Height="17" MinHeight="17"/>
<RowDefinition Height="17" MinHeight="17"/>
<RowDefinition Height="17" MinHeight="17"/>
<RowDefinition Height="17" MinHeight="17"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="55" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Padding="0,0,5,0" Text="Type:" TextAlignment="Right" />
<TextBlock Grid.Column="1" Padding="0" Text="{Binding Path=AType}" Grid.ColumnSpan="2" />
<TextBlock Grid.Row ="1" Grid.Column="0" Padding="0,0,5,0" Text="Address 1:" TextAlignment="Right" />
<!-- List Of Similar Fields ->
<Grid x:Name="grdAddressEditOptions"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="Auto"
Width="Auto"
Grid.Column="3"
Grid.RowSpan="7" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button x:Name="btnEdit"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="Auto"
Width="Auto"
Grid.Row="0"
Padding="4,5,4,8"
Margin="0,8,10,0"
Command="{Binding Value, Source={StaticResource EditAddressCommand}}"
CommandParameter="{Binding}" >
<Button.Content>
<Image x:Name="btnEditIcon"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Height="Auto"
Width="Auto"
Source="/Insight.ModuleUser;component/Images/edit.png"
Visibility="Visible" />
</Button.Content>
</Button>
<Button x:Name="btnDelete"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="Auto"
Width="Auto"
Grid.Row="2"
Padding="4,5,4,8"
Margin="0,0,10,5"
Command="{Binding Value, Source={StaticResource DeleteAddressCommand}}"
CommandParameter="{Binding}" >
<Button.Content>
<Image x:Name="btnDeleteIcon"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Height="Auto"
Width="Auto"
Source="/Insight.ModuleUser;component/Images/delete.png"
Visibility="Visible" />
</Button.Content>
</Button>
</Grid>
</Grid>
</Border>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</Grid>
</UserControl>
AddressListView code behind:
Imports System.Collections.ObjectModel
Imports Insight.DataServices.Primitives
Partial Public Class AddressListView
Inherits UserControl
Public ReadOnly AddressesProperty As DependencyProperty = DependencyProperty.Register("Addresses", GetType(ObservableCollection(Of Address)), GetType(AddressListView), New PropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf OnAddressesChanged)))
Public Sub New()
InitializeComponent()
End Sub
Public Property Addresses As ObservableCollection(Of Address)
Get
Return DirectCast(GetValue(AddressesProperty), ObservableCollection(Of Address))
End Get
Set(value As ObservableCollection(Of Address))
SetValue(AddressesProperty, value)
End Set
End Property
Public Sub OnAddressesChanged()
Me.dgAddresses.ItemsSource = Addresses
End Sub
End Class
Base page:
<UserControl x:Class="Insight.ModuleUser.Views.EditUserView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Interactivity.InteractionRequest;assembly=Microsoft.Practices.Prism.Interactivity"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cm="clr-namespace:System.ComponentModel;assembly=System.Windows"
xmlns:data="clr-namespace:System.Windows.Data;assembly=System.Windows"
xmlns:vm="clr-namespace:Insight.ModuleUser.ViewModels"
xmlns:command="clr-namespace:PrismFramework.Implementors.Commanding;assembly=PrismFramework"
xmlns:controls="clr-namespace:Insight.Controls;assembly=Insight.Controls"
xmlns:modalDialogs="clr-namespace:Insight.Controls.ModalDialogViews;assembly=Insight.Controls"
mc:Ignorable="d"
d:DesignHeight="500" d:DesignWidth="1144"
d:DataContext="{d:DesignData /Insight.ModuleUser;component/SampleData/EditUserViewModelSampleData.xaml}">
<UserControl.Resources>
<command:ObservableCommand x:Name="OpenProjectCommand" Value="{Binding OpenProjectCmd}"/>
<command:ObservableCommand x:Name="OpenPaymentCommand" Value="{Binding OpenPaymentCmd}"/>
<command:ObservableCommand x:Name="OpenInvoiceCommand" Value="{Binding OpenInvoiceCmd}"/>
<command:ObservableCommand x:Name="OpenPaymentItemCommand" Value="{Binding OpenPaymentItemCmd}"/>
<command:ObservableCommand x:Name="EditPhoneCommand" Value="{Binding EditPhoneNumberCmd}"/>
<command:ObservableCommand x:Name="DeletePhoneCommand" Value="{Binding DeletePhoneNumberCmd}"/>
<command:ObservableCommand x:Name="EditEmailAddressCommand" Value="{Binding EditEmailAddressCmd}"/>
<command:ObservableCommand x:Name="DeleteEmailAddressCommand" Value="{Binding DeleteEmailAddressCmd}"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" >
<controls:AddressListView x:Name="ctrlAddressListView"
Addresses="{Binding User.Addresses}" />
</Grid>
</UserControl>
This seems a perfectly reasonable approach. However, you could use binding in your user control view, rather than setting the items source in code.
To do this you need to set the DataContext of the user control to be your user control type. This could be done either in the code behind for the user control (setting this.DataContext = this), or through element binding in XAML:
<UserControl
...
x:Name="MyName"
DataContext="{Binding ElementName=MyName}"
However, my approach would be not to use a user control at all, as all you're really talking about is view composition and reusing a particular section of the view between other views.
View composition is extremely straightforward with an MVVM framework such as Caliburn.Micro. In this case you would have an AddressViewModel and AddressView, and use a ContentControl to inject the AddressView into the base view:
<ContentControl x:Name="AddressViewModel" />

Resources