原作者:
https://community.dynamics.com/ax/b/axilicious/archive/2013/05/20/hosting-custom-wpf-calendar-control-in-ax-2012.aspx

Hosting custom WPF calendar control in AX 2012

RATE THIS 

KENNY SAELEN 

20 MAY 2013 5:31 AM 

The requirement

A couple of days ago, I needed to create a calendar like lookup but it had to be possible to block out dates for selection. In Dynamics AX, there is calendar lookup form available, but there are some minors to it:

  • It is not really (easy) customizable
  • It has limited functionality

What I needed the calendar form to do:

  • Visually block dates for selection
  • Easy navigation over longer periods
  • Support for different selection types (Single date, weeks, multiple selected dates, …)
  • Provide a custom range to display dates

So it is clear that Ax does not have a calendar like that, so I had the idea of creating a custom control for this. But due to time restriction ( isn't there always a time restriction !  ) I went looking for a WPF control that I could wrap around and integrate it with Ax. And then I found the following control that has all I need : http://www.codeproject.com/Articles/88757/WPF-Calendar-and-Datepicker-Final But having a working control in a WPF application is one thing, getting it to work with Dynamics AX is another. I noticed when I was using the control directly, the client crashed and some of the properties were not marshallable because Dynamics AX does not support generics. So wrapping the control in a control of my own was an option here. And there are two reasons why I did wrap the control:

  • When using the control directly, it crashed 
  • When wrapping into your own control, you can decide which of the features you want to be available and which are omitted
  • It is possible to write some helper methods for generic properties since Ax does not support generics

Now let me show you how to do it.

Creating a custom WPF user control

First start of by creating a custom WPF user control. Open up visual studio and create a new project of the type WPF User Control Library

Since we are going to implement the vhCalendat control, add a reference to the vhCalendar dll file. (Found in the CodeProject post link above). Once the reference is in place and before going into XAML mode, let us take a look at the code behind file and put some things in place.

Control definition

First we have a partial class that defines the CalendarViewControl. Extending this later with your own stuff should be easy since it is a partial. public partial class CalendarViewControl : UserControl, INotifyPropertyChanged

Control properties

Now lets add properties for all of the calendar's properties that we want to expose. (Here I will not show them all, just some examples)

/// <summary>

/// Gets/Sets the date that is being displayed in the calendar

/// </summary>

public DateTime DisplayDate

{

get

{

return
(DateTime)theCalendar.DisplayDate;

}

set

{

theCalendar.DisplayDate =
value;

RaisePropertyChanged("DisplayDate");

}

}

 
 

/// <summary>

/// Gets/Sets animations are used

/// </summary>

public
bool IsAnimated

{

get

{

return
(bool)theCalendar.IsAnimated;

}

set

{

theCalendar.IsAnimated =
value;

RaisePropertyChanged("IsAnimated");

}

}

 
 

/// <summary>

/// Gets/Sets the selection mode

/// </summary>

public CalendarSelectionType SelectionMode

{

get

{

int i =
(int)theCalendar.SelectionMode;

return
(CalendarSelectionType)i;

}

set

{

int i =
(int)theCalendar.SelectionMode;

theCalendar.SelectionMode =
(SelectionType)i;

RaisePropertyChanged("SelectionMode");

}

}

The last property above shows a bit of 'nasty' code because I needed to translate the System.Windows.Visibility enum into a custom enum. This was because the CIL generator had a problem with the System.Windows.Visibility enum. CIL kept giving me the error : 'Invalid cast' until I used a custom enum.

Control events

Next to some properties, there are also a couple of events. The first one is to let Dynamics AX know that the selected date has changed.

public
delegate
void SelectedDateChangedEventHandler(object sender, EventArgs e);

 
 

public
event SelectedDateChangedEventHandler SelectedDateChanged;

 
 

/// <summary>

/// Raised when the selected date changes in the calendar.

/// </summary>

public
void RaiseSelectedDateChanged(object sender, vhCalendar.SelectedDateChangedEventArgs e)

{

if
(SelectedDateChanged !=
null)

{

SelectedDateChanged(this, new EPSSelectedDateChangedEventArgs()
{ NewDate = e.NewDate, OldDate = e.OldDate });

}

}

Please note that the delegate is outside of the class but in the same namespace. Af of now, I've implemented single selection but there are also events in the calendar for when multiple selection changes.

Another important one is the RaisePropertyChanged event. This event will be useful to us when we are using binding in XAML. It will notify the control's user interface that a property has changed and all of the UI elements that are bound to the corresponding property will be updated. (Note that the delegate PropertyChangedEventHandler is already known because our control implement INotifyPropertyChanged.

/// <summary>

/// Raised when one of the properties was changed in the WPF control

/// </summary>

public
void RaisePropertyChanged(string propertyName)

{

if
(PropertyChanged !=
null)

{

PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

}

}

Implement the control in Dynamics AX

Now for the AX part, start with creating a form with a ManagedHost control so that we can host the control. When you add the ManagedHost control, a windows will popup to select the control type that you want to use. In that window, select the reference to the dll (or browse for your dll) and select the type of control to use.

 Next we need a way to handle the events of the calencar control. When we choose a date in the control, we need to be notified in X++ so that we can do what we want with it.

To do that, you can right click the ManagedHost control and select events. In the events window, you can now select our selectedDateChanged event and add an X++ handler method to it.

  The following code was added to our form ( Note that you can also use the ManagedEventHandler class when you are consuming external services asynchronously! ):

_ManagedHost_Control = ManagedHost.control();

_ManagedHost_Control.add_SelectedDateChanged(new ManagedEventHandler(this,
'ManagedHost_SelectedDateChanged'));

So when we open our form and we select a date in the calendar, the ManagedHost_SelectedDateChanged method will be called. So let's open up the form and see what it looks like.

Nice! But are we satisfied yet? Not quite…

Imho when implementing things like this, you should always keep in mind that others want to use your control in other contexts. Today I want to use it when selecting delivery dates but if we make some modifications, everyone can use this in any context they want.

What I did to achieve this is to create two things:

  • A property bag class that contains all of the properties that can be passed to the control.
  • An interface that forms need to implement when they want to host the control.

So first let's take a look at the property bag. This is in fact merely a class with some parm methods on it. No PhD required here 

/// <summary>

/// Data container class containing the parameters that can be set on the WPF control from inside Ax.

/// </summary>

public
class EPSWPFCalendarParameters

{

// Selection type of dates (single, multiple, ...)

EPS.AX.WPFControls.CalendarSelectionType selectionType;

 
 

// Start view of the calendar (month, year, decade, ...)

EPS.AX.WPFControls.CalendarDisplayType displayType;

 
 

// Date being displayed in the calendar

System.DateTime displayDate;

 
 

// Date from where the calendar displays dates

System.DateTime displayDateStart;

 
 

// Date to where the calendar displays dates

System.DateTime displayDateEnd;

 
 

// Use animation and transitions in the calendar

boolean isAnimated;

 
 

// Show the footer

EPS.AX.WPFControls.CalendarVisibilityType footerVisibility;

 
 

// Color the today's date in the calendar

boolean isTodayHighlighted;

 
 

// Show week columns

EPS.AX.WPFControls.CalendarVisibilityType weekColumnVisibility;

 
 

// Dates that will be marked as blocked and will not be selectable in the calendar

Map blockedDatesList;

}

Apart from the parm methods, there is also an initialization method present so that some defaults are set in case they are not specified by the caller.

public
void initDefaults()

{

;

this.parmDisplayDate
(systemDateGet());

this.parmDisplayType
(EPS.AX.WPFControls.CalendarDisplayType::Month);

this.parmFooterVisibility
(EPS.AX.WPFControls.CalendarVisibilityType::Visible);

this.parmIsAnimated
(true);

this.parmIsTodayHighlighted
(true);

this.parmSelectionType
(EPS.AX.WPFControls.CalendarSelectionType::Single);

this.parmWeekColumnVisibility
(EPS.AX.WPFControls.CalendarVisibilityType::Visible);

this.parmBlockedDatesList
(new Map(Types::Date, Types::Date));

}

Now for the next part, let's create an interface that simply asures that forms hosting the control have a method to construct parameters for the calendar control.

/// <summary>

/// Interface for defining what is needed to be able to host the EPS WPF Calendar control on that form.

/// </summary>

public
interface EPSWPFCalendarFormHostable {
}

 
 

/// <summary>

/// Calendar parameter data bag.

/// </summary>

/// <returns>

/// An instance of EPSWPFCalendarParameters

/// </returns>

public EPSWPFCalendarParameters calendarParameters()

{
}

This interface needs to be implemented on the caller form. Below we have an example of just that. (Note that for this example the code is on the form, but this should be done in a form handler class)

class FormRun extends ObjectRun implements EPSWPFCalendarFormHostable

 
 

/// <summary>

/// Contains parameters for the WPF calendar

/// <summary>

/// <returns>

/// An instance of EPSWPFCalendarParameters.

/// </returns>

public EPSWPFCalendarParameters calendarParameters()

{

;

// Create default instance of the parameters

calendarParameters = EPSWPFCalendarParameters::construct();

 
 

// Set the calendar selection mode to a single date

calendarParameters.parmSelectionType(

EPS.AX.WPFControls.CalendarSelectionType::Single);

 
 

// Set the default view to a month

calendarParameters.parmDisplayType(

EPS.AX.WPFControls.CalendarDisplayType::Month);

 
 

// Passes a map of dates to be blocked for selection in the calendar

calendarParameters.parmBlockedDatesList(

this.parmBlockedDeliveryDates());

 
 

return calendarParameters;

}

For the last part, we need to glue these together. This is done in the form where we host the calendar control. When the form is called, it requests the calendar parameters from the caller and applies all of them to the control.

public
void init()

{

SysSetupFormRun formRun = element.args().caller();

;

super();

 
 

// Check if the managed host is even the right type

if(ManagedHost.control() is EPS.AX.WPFControls.CalendarViewControl)

{

calendarViewControl = ManagedHost.control() as EPS.AX.WPFControls.CalendarViewControl;

}

else

{

throw error("@EPS6008");

}

 
 

// Initialize from the caller interface (The caller should have implemented the interface that contains the calendarParameters)

if(formHasMethod(formRun,
'calendarParameters'))

{

hostable = element.args().caller();

 
 

// The hostable should have the parameters defined

calendarParameters = hostable.calendarParameters();

 
 

// Initialize the calendar control based on the found parameters

element.initControlParameters();

}

else

{

throw error("@EPS6009");

}

 
 

// Register an event handler that attached to the selected date changed event of the calendar control)

calendarViewControl.add_SelectedDateChanged(new ManagedEventHandler(this,
'ManagedHost_SelectedDateChanged'));

}

Note the formHasMethod call. This is an extra safety check. When using classes, the compiler makes sure that you have implemented the interface's methods. But with forms, you never have a constructor called so that the compiler can do the check for you.

/// <summary>

/// Initializes the control based on the parameters found in the caller.

/// <summary>

private
void initControlParameters()

{

Map blockedDates = calendarParameters.parmBlockedDatesList();

MapEnumerator mapEnum;

TransDate fromDate;

TransDate toDate;

;

 
 

calendarViewControl.set_SelectionMode
(calendarParameters.parmSelectionType());

calendarViewControl.set_DisplayMode
(calendarParameters.parmDisplayType());

calendarViewControl.set_FooterVisibility
(calendarParameters.parmFooterVisibility());

calendarViewControl.set_IsAnimated
(calendarParameters.parmIsAnimated());

calendarViewControl.set_IsTodayHighlighted
(calendarParameters.parmIsTodayHighlighted());

calendarViewControl.set_WeekColumnVisibility
(calendarParameters.parmWeekColumnVisibility());

 
 

if(calendarParameters.parmDisplayDate())

{

calendarViewControl.set_DisplayDate
(calendarParameters.parmDisplayDate());

}

 
 

if(calendarParameters.parmDisplayDateStart())

{

calendarViewControl.set_DisplayDateStart
(calendarParameters.parmDisplayDateStart());

}

 
 

if(calendarParameters.parmDisplayDateEnd())

{

calendarViewControl.set_DisplayDateEnd
(calendarParameters.parmDisplayDateEnd());

}

 
 

// Pass the blocked out dates collection to the control.

if(blockedDates)

{

mapEnum = blockedDates.getEnumerator();

while
(mapEnum.moveNext())

{

fromDate = mapEnum.currentKey();

toDate = mapEnum.currentValue();

calendarViewControl.addBlockedDate(fromDate, toDate);

}

}

}

So when all of this is in place, the calendar form calls the calling form and requests the parameters, passes them to the control and renders the control. Everyone that want to have the same control hosted on their for only need to implement the calendar parameters method on the form and that's it.

When all is in place, this is what it looks like when the calendar has blocked days:

 
 

When clicking on the month title, you can easily navigate between months and years:

 
 

 
 

So that is the end of it. I hope you already have some ideas as to what controls you can now host within Dynamics AX 2012. It is possible to create your own, but you can host any other WPF control if you want. (Think of Infragistics controls, Telerik, …)

Hosting custom WPF calendar control in AX 2012的更多相关文章

  1. Overview of Form Control Types [AX 2012]

    Overview of Form Control Types [AX 2012] Other Versions 0 out of 1 rated this helpful - Rate this to ...

  2. Tutorial: WPF User Control for AX2012

    原作者: https://community.dynamics.com/ax/b/goshoom/archive/2011/10/06/tutorial-wpf-user-control-for-ax ...

  3. ClassLibary和WPF User Control LIbary和WPF Custom Control Libary的异同

    说来惭愧,接触WPF这么长时间了,今天在写自定义控件时遇到一个问题:运行界面中并没有显示自定义控件,经调试发现原来没有加载Themes中的Generic.xaml. 可是为什么在其他solution中 ...

  4. Extended Data Type Properties [AX 2012]

    Extended Data Type Properties [AX 2012] This topic has not yet been rated - Rate this topic Updated: ...

  5. Select Statement Syntax [AX 2012]

    Applies To: Microsoft Dynamics AX 2012 R3, Microsoft Dynamics AX 2012 R2, Microsoft Dynamics AX 2012 ...

  6. Using Controls in a Form Design [AX 2012]

    Using Controls in a Form Design [AX 2012] This topic has not yet been rated - Rate this topic Update ...

  7. Table Properties [AX 2012]

    Table Properties [AX 2012] 1 out of 2 rated this helpful - Rate this topic Updated: July 20, 2012 Ap ...

  8. How to: Enable and Disable an Action Pane Button on a List Page [AX 2012]

    Applies To: Microsoft Dynamics AX 2012 R2, Microsoft Dynamics AX 2012 Feature Pack, Microsoft Dynami ...

  9. [eBook]Inside Microsoft Dynamics AX 2012 R3发布

    最近一本关于Microsoft Dynamics AX 2012开发的书<Inside Microsoft Dynamics AX 2012 R3> 发布. Book Descriptio ...

随机推荐

  1. 升级openssl

    升级openssl 依赖openssl的软件,如果是静态编译openssl,那么需要重新编译软件,如果是利用openssl的so动态库,那么只需要替换一下so文件并重启软件即可 openssh也依赖o ...

  2. linux 文件权限 初级

    sudo lsattr /etc/sudoers  查看属性 sudo vi index.html 以root权限编辑文件

  3. Power-BI助顾得医药济世康民

    公司简介成立于 2011 年 9 月 24 日,是一家主要以医院销售为主,集批发.配送.售后服务于一体的商业公司.现有药品储备面积 16000 平方米,开户医院 52 家,营销网络辐射山西省境内部分县 ...

  4. Java开发遇到的问题及解决方案

    一.java.lang.OutOfMemoryError 问题:myeclipse 内存不足,又显示内存溢出等问题怎么回事?( java.lang.OutOfMemoryError: PermGen ...

  5. 删除SSMS中保存的帐户信息

    通常我们在对象资源管理器中连接服务器时,会发现在服务器名称下保存有之前的实例信息.随着连接增多,要找某个连接还得费劲.sql2012:此时可以删除C:\Users\Administrator\AppD ...

  6. python安装MySQLdb驱动

    安装了好几次了,索性几个笔记吧,经常因为返回gcc报错 安装 python-devel包即可

  7. TNS-01251: Cannot set trace/log directory under ADR

    试图改变监听日志的名称时,报出TNS-01251错误: $ lsnrctl LSNRCTL - Production on -JUN- :: Copyright (c) , , Oracle. All ...

  8. UITextFiled

    - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica ...

  9. something about css locating.

    CSS position:static:默认属性,静态定位relative:相对定位,相对于父元素的定位,需要配合top,left,right,bottom,z-index等属性absolute:绝对 ...

  10. [原创]java WEB学习笔记89:Hibernate学习之路-- -Hibernate检索方式(5种),HQL介绍,实现功能,实现步骤,

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...