I have to do a task hard for me and maybe you can help me. I've tried to design a window dialog to update data from a database view. This database view contains integer, DateTime, string, Time, Boolean properties.
First of all, I load the database view data into a GridControl
. The GridControl
is readonly
not editable. I configure the columns of my GridControl
dynamically and this configuration is located in a database table called FieldDescription
.
When the end-user doubleclicks on a cell, this window dialog pops up. This dialog shows a Combobox
when the type of the column is enum
or bit (boolean
) and otherwise a TextEdit
as default content.Notice for example that I know that a GridColumn
represents an enum
type because of the FieldDescription
. Because the type of this database view property bound to this GridColumn
is an integer (specifically the value)
The code of this window dialog is (I use DevExpress controls):
<!-- DataTemplates -->
<DataTemplate x:Key="DefaultTemplate">
<dxe:TextEdit Name="TxtNewValue" Grid.Column="0" Margin="0"
HorizontalAlignment="Stretch"
Text="{Binding TargetValue, UpdateSourceTrigger=PropertyChanged}"
Mask="{Binding TargetFieldDescription.fdValidateExp}"
Loaded="FocusTextEditOnLoad"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding TargetFieldType}" Value="{x:Static eva:FieldDataType.Date}">
<Setter TargetName="TxtNewValue" Property="MaskType" Value="DateTime" />
</DataTrigger>
<DataTrigger Binding="{Binding TargetFieldType}" Value="{x:Static eva:FieldDataType.DatTime}">
<Setter TargetName="TxtNewValue" Property="MaskType" Value="DateTime" />
</DataTrigger>
<DataTrigger Binding="{Binding TargetFieldType}" Value="{x:Static eva:FieldDataType.Time}">
<Setter TargetName="TxtNewValue" Property="MaskType" Value="RegEx" />
</DataTrigger>
<DataTrigger Binding="{Binding TargetFieldType}" Value="{x:Static eva:FieldDataType.Num}">
<Setter TargetName="TxtNewValue" Property="MaskType" Value="RegEx" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate x:Key="ComboboxTemplate">
<dxe:ComboBoxEdit ItemsSource="{Binding EnumItemsSource}"
DisplayMember="Name"
ValueMember="Id"
IsTextEditable="False"
EditValue="{Binding TargetValue, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
<!-- Select a Datatemplate depending on the type of the Enum FieldDataType-->
<DataTemplate DataType="{x:Type massedit:SimpleFieldVM}">
<ContentControl Content="{Binding .}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<!-- Without selector, only with DataTriggers select my ContentControl -->
<DataTrigger Binding="{Binding TargetFieldType}" Value="{x:Static eva:FieldDataType.ENum}">
<Setter Property="ContentTemplate" Value="{StaticResource ComboboxTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding TargetFieldType}" Value="{x:Static eva:FieldDataType.Bit}">
<Setter Property="ContentTemplate" Value="{StaticResource ComboboxTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</Window.Resources>
<!-- Layout -->
<Grid x:Name="LayoutRoot" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<ContentControl Name="MainLayout" Content="{Binding}" />
</Grid>
I have only one ViewModel
to provide the values to this Dialog Window. My ViewModel
has an enum FieldDataType
property called TargetFieldType
. It contains the type of the doubleclicked GridColumn
. This value is an enum
(FieldDataType.DatTime
, FieldDataType.Time
, FieldDataType.Date
, FieldDataType.Bit
, FieldDataType.Num
, FieldDataType.Enum
, FieldDataType.String
).
The ItemsSource
of the ComboBox
is bound with the ViewModel
property:
public List<EnumItemObject> EnumItemsSource
Depending on the type of the column I prepare the data and I convert an enum
of my model, in List<EnumItemObject>
, aboolean
property also in List<EnumItemObject>
(Yes, No). But my ViewModel
has to prepare the data depending on the FieldDataType
. Maybe I can do more things in the XAML code to save a lot of code to prepare the provided data to my view, or separating my current ViewModel
into multiple ViewModel
s, to do this clearly.
It is difficult to explain the whole task but maybe you can help and tell me if my approach is correct or too difficult. I hope you understand what I mean.
XAML code:
<!-- DataContext definition -->
<Window.DataContext>
<massedit:SimpleFieldVM/>
</Window.DataContext>
<!-- Behaviors -->
<i:Interaction.Behaviors>
<behavior:WindowBehavior x:Name="WindowBehavior" OnOk="{Binding DoOnOk}" OnCancel="{Binding DoOnCancel}"/>
</i:Interaction.Behaviors>
<!-- Resources -->
<Window.Resources>
<!-- DataTemplates -->
<DataTemplate x:Key="DefaultTemplate">
<dxe:TextEdit Name="TxtNewValue" Grid.Column="0" Margin="0"
HorizontalAlignment="Stretch"
Text="{Binding TargetValue, UpdateSourceTrigger=PropertyChanged}"
Mask="{Binding TargetFieldDescription.fdValidateExp}"
Loaded="FocusTextEditOnLoad"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding TargetFieldType}" Value="{x:Static eva:FieldDataType.Date}">
<Setter TargetName="TxtNewValue" Property="MaskType" Value="DateTime" />
</DataTrigger>
<DataTrigger Binding="{Binding TargetFieldType}" Value="{x:Static eva:FieldDataType.DatTime}">
<Setter TargetName="TxtNewValue" Property="MaskType" Value="DateTime" />
</DataTrigger>
<DataTrigger Binding="{Binding TargetFieldType}" Value="{x:Static eva:FieldDataType.Time}">
<Setter TargetName="TxtNewValue" Property="MaskType" Value="RegEx" />
</DataTrigger>
<DataTrigger Binding="{Binding TargetFieldType}" Value="{x:Static eva:FieldDataType.Num}">
<Setter TargetName="TxtNewValue" Property="MaskType" Value="RegEx" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate x:Key="ComboboxTemplate">
<dxe:ComboBoxEdit ItemsSource="{Binding EnumItemsSource}"
DisplayMember="Name"
ValueMember="Id"
IsTextEditable="False"
EditValue="{Binding TargetValue, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
<!-- Select a Datatemplate depending on the type of the Enum FieldDataType-->
<DataTemplate DataType="{x:Type massedit:SimpleFieldVM}">
<ContentControl Content="{Binding .}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<!-- Without selector, only with DataTriggers select my ContentControl -->
<DataTrigger Binding="{Binding TargetFieldType}" Value="{x:Static eva:FieldDataType.ENum}">
<Setter Property="ContentTemplate" Value="{StaticResource ComboboxTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding TargetFieldType}" Value="{x:Static eva:FieldDataType.Bit}">
<Setter Property="ContentTemplate" Value="{StaticResource ComboboxTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</Window.Resources>
<!-- Layout -->
<Grid x:Name="LayoutRoot" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<ContentControl Name="MainLayout" Content="{Binding}" />
<dx:UniformStackPanel Grid.Column="1" Orientation="Vertical" ChildSpacing="0">
<Button Command="{Binding ElementName=WindowBehavior, Path=OnCancelCommand}" Width="18" Height="18" Padding="-1" Background="Transparent" BorderBrush="Transparent">
<Button.Content>
<Image Source="../images/cancel-16x16.png"/>
</Button.Content>
</Button>
<Button Command="{Binding ElementName=WindowBehavior, Path=OnOkCommand}" Width="18" Height="18" Padding="-1" Background="Transparent" BorderBrush="Transparent">
<Button.Content>
<Image Source="../images/ok-16x16.png"/>
</Button.Content>
</Button>
</dx:UniformStackPanel>
</Grid>
ViewModel
code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using DevExpress.Data.Filtering;
using DevExpress.Xpo;
using DevExpress.Xpo.DB;
using EVA.Types;
using Model.Eva;
using Model.Eva.EvaDataModelCode;
using Utilities.Eva;
namespace EVAGui.MassEdit
{
/// <summary>
/// ViewModel for view SimpleFieldDialog. Provides values that need the view. (From this view can be edited simple types
/// (uses defaulttemplate and Enumtemplate) )
/// </summary>
public class SimpleFieldVM : NotificationObject
{
#region properties
private Action _doOnOk;
private Action _doOnCancel;
private object _targetValue;
private List<EnumItemObject> _enumItemsSource;
private IList<DbvBigView> _selectedItems;
private DbvBigView _firstSelectedItem;
private DbFieldDescription _targetFieldDescription;
/// <summary>
/// Action is executed when button ok is clicked
/// </summary>
public Action DoOnOk
{
get { return _doOnOk; }
set
{
_doOnOk = value;
OnPropertyChanged();
}
}
/// <summary>
/// Action is executed when button cancel is clicked
/// </summary>
public Action DoOnCancel
{
get { return _doOnCancel; }
set
{
_doOnCancel = value;
OnPropertyChanged();
}
}
/// <summary>
/// Contains the new value to be saved in database
/// </summary>
public object TargetValue
{
get { return _targetValue; }
set
{
_targetValue = value;
OnPropertyChanged();
}
}
/// <summary>
/// Contains the enum value of the kind of type of the field that should be edited
/// </summary>
public FieldDataType TargetFieldType { get; set; }
/// <summary>
/// Contains the ItemsSource for Enums
/// </summary>
public List<EnumItemObject> EnumItemsSource
{
get { return _enumItemsSource; }
set
{
_enumItemsSource = value;
OnPropertyChanged();
}
}
/// <summary>
/// Contains the field description of the property of BigView that is being edited
/// </summary>
public DbFieldDescription TargetFieldDescription
{
get { return _targetFieldDescription; }
set
{
_targetFieldDescription = value;
if (_targetFieldDescription != null)
TargetFieldType = (FieldDataType)_targetFieldDescription.fdDataType;
else
TargetFieldType = FieldDataType.None;
}
}
/// <summary>
/// Selected elements in the MassEditGrid
/// </summary>
public IList<DbvBigView> SelectedItems
{
get { return _selectedItems; }
set
{
_selectedItems = value;
if (_selectedItems.Any())
FirstSelectedItem = _selectedItems.ToList()[0];
}
}
/// <summary>
/// Contains the first selected element. In this view is displayed the data of the first element and
/// will be applied to all selected items
/// </summary>
protected DbvBigView FirstSelectedItem
{
get { return _firstSelectedItem; }
set
{
_firstSelectedItem = value;
if (_firstSelectedItem != null && TargetFieldDescription != null)
LookUpViewData();
}
}
#endregion
#region constructor
/// <summary>
/// constructor of this ViewModel. Defines the delegate for Ok Action (save changes)
/// Ok Action will save the changes in Database
/// </summary>
public SimpleFieldVM()
{
DoOnOk = () => DbvBigViewUtils.SaveSimpleData(SelectedItems, TargetFieldDescription,
TargetValue);
//DoOnOk = () => PersistentDataManager.SaveData();
}
#endregion
/// <summary>
/// Initializes the ViewModel with the FieldDescription of the field that will be updated and the selected BidView elements in MassEditView
/// before this dialog pops up
/// </summary>
/// <param name="items">Selected BigView elements in MassEditView</param>
/// <param name="fieldDescription">FieldDescription of the current field</param>
public void InitializeModelView(IList<DbvBigView> items, DbFieldDescription fieldDescription)
{
TargetFieldDescription = fieldDescription;
SelectedItems = items;
}
/// <summary>
/// Loads the data to be displayed in the view
/// </summary>
private void LookUpViewData()
{
var propertyInfo = _firstSelectedItem.GetType().GetProperty(TargetFieldDescription.fdBigViewColumnName);
if ((propertyInfo != null) && (propertyInfo.GetValue(_firstSelectedItem) != null))
{
if ((int) FieldDataType.ENum == TargetFieldDescription.fdDataType)
PrepareDataForEnumTemplate(propertyInfo);
if ((int) FieldDataType.Bit == TargetFieldDescription.fdDataType)
PrepareDataForBitTemplate(propertyInfo); // like EnumTemplate
else if ((int) FieldDataType.Time == TargetFieldDescription.fdDataType)
PrepareDataForTimeTemplate(propertyInfo);
else
PrepareDataForDefaultTemplate(propertyInfo);
}
else
TargetValue = String.Empty;
}
/// <summary>
/// Sets the value to the properties for the DefaultTemplate view
/// </summary>
/// <param name="propertyInfo"></param>
private void PrepareDataForTimeTemplate(PropertyInfo propertyInfo)
{
var value = propertyInfo.GetValue(_firstSelectedItem);
var converter = new ConvertTimeDisplay();
TargetValue = converter.Convert(value, null, null, null);
}
/// <summary>
/// Sets the value to the properties for the BitTemplate view. (similar with EnumTemplate)
/// </summary>
/// <param name="propertyInfo">a boolean property</param>
private void PrepareDataForBitTemplate(PropertyInfo propertyInfo)
{
TargetValue = (int)propertyInfo.GetValue(_firstSelectedItem);
EnumItemsSource = new List<EnumItemObject>();
EnumItemsSource.Add(new EnumItemObject() { Id = 0, Name = "Nein" });
EnumItemsSource.Add(new EnumItemObject() { Id = 1, Name = "Ja" });
}
/// <summary>
/// Sets the value to the properties for the DefaultTemplate view
/// </summary>
/// <param name="propertyInfo"></param>
private void PrepareDataForDefaultTemplate(PropertyInfo propertyInfo)
{
TargetValue = propertyInfo.GetValue(_firstSelectedItem).ToString();
}
/// <summary>
/// Sets the value to the properties for the EnumTemplate view
/// </summary>
/// <param name="propertyInfo"></param>
private void PrepareDataForEnumTemplate(PropertyInfo propertyInfo)
{
TargetValue = (int)propertyInfo.GetValue(_firstSelectedItem);
if (_targetFieldDescription.fdEnumSource != null)
{
switch (_targetFieldDescription.fdEnumSource)
{
case "Station":
EnumItemsSource = ConvertListToEnumItemObjectList(_targetFieldDescription.fdEnumSource);
break;
default:
EnumItemsSource = EnumUtil.ConvertEnumToEnumItemObjectList(_targetFieldDescription.fdEnumSource);
break;
}
}
}
/// <summary>
/// Returns a list of EnumItemObjects from a collection
/// </summary>
/// <param name="fdEnumSource">Type of objects to be converted in EnumItemObjects</param>
/// <returns>The list of EnumItemObjects</returns>
private List<EnumItemObject> ConvertListToEnumItemObjectList(string fdEnumSource)
{
switch (fdEnumSource)
{
case "Station":
return new XPCollection<DbStation>(EvaApplication.App.MainUnitOfWork,
CriteriaOperator.Parse("1=1"), new[] { new SortProperty("stExternalID", SortingDirection.Ascending) }).
Select(s=> new EnumItemObject(){Id=s.stID, Name = s.stGVLsysName}).
ToList();
default:
return null;
}
}
}
}