551 lines
25 KiB
C#
551 lines
25 KiB
C#
/*
|
|
* DesignSupport - Design time support for the various classes within ObjectListView
|
|
*
|
|
* Author: Phillip Piper
|
|
* Date: 12/08/2009 8:36 PM
|
|
*
|
|
* Change log:
|
|
* 2012-08-27 JPP - Fall back to more specific type name for the ListViewDesigner if
|
|
* the first GetType() fails.
|
|
* v2.5.1
|
|
* 2012-04-26 JPP - Filter group events from TreeListView since it can't have groups
|
|
* 2011-06-06 JPP - Vastly improved ObjectListViewDesigner, based off information in
|
|
* "'Inheriting' from an Internal WinForms Designer" on CodeProject.
|
|
* v2.3
|
|
* 2009-08-12 JPP - Initial version
|
|
*
|
|
* To do:
|
|
*
|
|
* Copyright (C) 2009-2014 Phillip Piper
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.Reflection;
|
|
using System.Windows.Forms;
|
|
using System.Windows.Forms.Design;
|
|
|
|
namespace BrightIdeasSoftware.Design
|
|
{
|
|
|
|
/// <summary>
|
|
/// Designer for <see cref="ObjectListView"/> and its subclasses.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// This designer removes properties and events that are available on ListView but that are not
|
|
/// useful on ObjectListView.
|
|
/// </para>
|
|
/// <para>
|
|
/// We can't inherit from System.Windows.Forms.Design.ListViewDesigner, since it is marked internal.
|
|
/// So, this class uses reflection to create a ListViewDesigner and then forwards messages to that designer.
|
|
/// </para>
|
|
/// </remarks>
|
|
public class ObjectListViewDesigner : ControlDesigner
|
|
{
|
|
|
|
#region Initialize & Dispose
|
|
|
|
/// <summary>
|
|
/// Initializes the designer with the specified component.
|
|
/// </summary>
|
|
/// <param name="component">The <see cref="T:System.ComponentModel.IComponent"/> to associate the designer with. This component must always be an instance of, or derive from, <see cref="T:System.Windows.Forms.Control"/>. </param>
|
|
public override void Initialize(IComponent component) {
|
|
// Debug.WriteLine("ObjectListViewDesigner.Initialize");
|
|
|
|
// Use reflection to bypass the "internal" marker on ListViewDesigner
|
|
// If we can't get the unversioned designer, look specifically for .NET 4.0 version of it.
|
|
Type tListViewDesigner = Type.GetType("System.Windows.Forms.Design.ListViewDesigner, System.Design") ??
|
|
Type.GetType("System.Windows.Forms.Design.ListViewDesigner, System.Design, " +
|
|
"Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
|
|
if (tListViewDesigner == null) throw new ArgumentException("Could not load ListViewDesigner");
|
|
|
|
this.listViewDesigner = (ControlDesigner)Activator.CreateInstance(tListViewDesigner, BindingFlags.Instance | BindingFlags.Public, null, null, null);
|
|
this.designerFilter = this.listViewDesigner;
|
|
|
|
// Fetch the methods from the ListViewDesigner that we know we want to use
|
|
this.listViewDesignGetHitTest = tListViewDesigner.GetMethod("GetHitTest", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
this.listViewDesignWndProc = tListViewDesigner.GetMethod("WndProc", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
|
|
Debug.Assert(this.listViewDesignGetHitTest != null, "Required method (GetHitTest) not found on ListViewDesigner");
|
|
Debug.Assert(this.listViewDesignWndProc != null, "Required method (WndProc) not found on ListViewDesigner");
|
|
|
|
// Tell the Designer to use properties of default designer as well as the properties of this class (do before base.Initialize)
|
|
TypeDescriptor.CreateAssociation(component, this.listViewDesigner);
|
|
|
|
IServiceContainer site = (IServiceContainer)component.Site;
|
|
if (site != null && GetService(typeof(DesignerCommandSet)) == null) {
|
|
site.AddService(typeof(DesignerCommandSet), new CDDesignerCommandSet(this));
|
|
} else {
|
|
Debug.Fail("site != null && GetService(typeof (DesignerCommandSet)) == null");
|
|
}
|
|
|
|
this.listViewDesigner.Initialize(component);
|
|
base.Initialize(component);
|
|
|
|
RemoveDuplicateDockingActionList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a newly created component.
|
|
/// </summary>
|
|
/// <param name="defaultValues">A name/value dictionary of default values to apply to properties. May be null if no default values are specified.</param>
|
|
public override void InitializeNewComponent(IDictionary defaultValues) {
|
|
// Debug.WriteLine("ObjectListViewDesigner.InitializeNewComponent");
|
|
base.InitializeNewComponent(defaultValues);
|
|
this.listViewDesigner.InitializeNewComponent(defaultValues);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases the unmanaged resources used by the <see cref="T:System.Windows.Forms.Design.ControlDesigner"/> and optionally releases the managed resources.
|
|
/// </summary>
|
|
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources. </param>
|
|
protected override void Dispose(bool disposing) {
|
|
// Debug.WriteLine("ObjectListViewDesigner.Dispose");
|
|
if (disposing) {
|
|
if (this.listViewDesigner != null) {
|
|
this.listViewDesigner.Dispose();
|
|
// Normally we would now null out the designer, but this designer
|
|
// still has methods called AFTER it is disposed.
|
|
}
|
|
}
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes the duplicate DockingActionList added by this designer to the <see cref="DesignerActionService"/>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <see cref="ControlDesigner.Initialize"/> adds an internal DockingActionList : 'Dock/Undock in Parent Container'.
|
|
/// But the default designer has already added that action list. So we need to remove one.
|
|
/// </remarks>
|
|
private void RemoveDuplicateDockingActionList() {
|
|
// This is a true hack -- in a class that is basically a huge hack itself.
|
|
// Reach into the bowel of our base class, get a private field, and use that fields value to
|
|
// remove an action from the designer.
|
|
// In ControlDesigner, there is "private DockingActionList dockingAction;"
|
|
// Don't you just love Reflector?!
|
|
FieldInfo fi = typeof(ControlDesigner).GetField("dockingAction", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
if (fi != null) {
|
|
DesignerActionList dockingAction = (DesignerActionList)fi.GetValue(this);
|
|
if (dockingAction != null) {
|
|
DesignerActionService service = (DesignerActionService)GetService(typeof(DesignerActionService));
|
|
if (service != null) {
|
|
service.Remove(this.Control, dockingAction);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDesignerFilter overrides
|
|
|
|
/// <summary>
|
|
/// Adjusts the set of properties the component exposes through a <see cref="T:System.ComponentModel.TypeDescriptor"/>.
|
|
/// </summary>
|
|
/// <param name="properties">An <see cref="T:System.Collections.IDictionary"/> containing the properties for the class of the component. </param>
|
|
protected override void PreFilterProperties(IDictionary properties) {
|
|
// Debug.WriteLine("ObjectListViewDesigner.PreFilterProperties");
|
|
|
|
// Always call the base PreFilterProperties implementation
|
|
// before you modify the properties collection.
|
|
base.PreFilterProperties(properties);
|
|
|
|
// Give the listviewdesigner a chance to filter the properties
|
|
// (though we already know it's not going to do anything)
|
|
this.designerFilter.PreFilterProperties(properties);
|
|
|
|
// I'd like to just remove the redundant properties, but that would
|
|
// break backward compatibility. The deserialiser that handles the XXX.Designer.cs file
|
|
// works off the designer, so even if the property exists in the class, the deserialiser will
|
|
// throw an error if the associated designer actually removes that property.
|
|
// So we shadow the unwanted properties, and give the replacement properties
|
|
// non-browsable attributes so that they are hidden from the user
|
|
|
|
List<string> unwantedProperties = new List<string>(new string[] {
|
|
"BackgroundImage", "BackgroundImageTiled", "HotTracking", "HoverSelection",
|
|
"LabelEdit", "VirtualListSize", "VirtualMode" });
|
|
|
|
// Also hid Tooltip properties, since giving a tooltip to the control through the IDE
|
|
// messes up the tooltip handling
|
|
foreach (string propertyName in properties.Keys) {
|
|
if (propertyName.StartsWith("ToolTip")) {
|
|
unwantedProperties.Add(propertyName);
|
|
}
|
|
}
|
|
|
|
// If we are looking at a TreeListView, remove group related properties
|
|
// since TreeListViews can't show groups
|
|
if (this.Control is TreeListView) {
|
|
unwantedProperties.AddRange(new string[] {
|
|
"GroupImageList", "GroupWithItemCountFormat", "GroupWithItemCountSingularFormat", "HasCollapsibleGroups",
|
|
"SpaceBetweenGroups", "ShowGroups", "SortGroupItemsByPrimaryColumn", "ShowItemCountOnGroups"
|
|
});
|
|
}
|
|
|
|
// Shadow the unwanted properties, and give the replacement properties
|
|
// non-browsable attributes so that they are hidden from the user
|
|
foreach (string unwantedProperty in unwantedProperties) {
|
|
PropertyDescriptor propertyDesc = TypeDescriptor.CreateProperty(
|
|
typeof(ObjectListView),
|
|
(PropertyDescriptor)properties[unwantedProperty],
|
|
new BrowsableAttribute(false));
|
|
properties[unwantedProperty] = propertyDesc;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows a designer to add to the set of events that it exposes through a <see cref="T:System.ComponentModel.TypeDescriptor"/>.
|
|
/// </summary>
|
|
/// <param name="events">The events for the class of the component. </param>
|
|
protected override void PreFilterEvents(IDictionary events) {
|
|
// Debug.WriteLine("ObjectListViewDesigner.PreFilterEvents");
|
|
base.PreFilterEvents(events);
|
|
this.designerFilter.PreFilterEvents(events);
|
|
|
|
// Remove the events that don't make sense for an ObjectListView.
|
|
// See PreFilterProperties() for why we do this dance rather than just remove the event.
|
|
List<string> unwanted = new List<string>(new string[] {
|
|
"AfterLabelEdit",
|
|
"BeforeLabelEdit",
|
|
"DrawColumnHeader",
|
|
"DrawItem",
|
|
"DrawSubItem",
|
|
"RetrieveVirtualItem",
|
|
"SearchForVirtualItem",
|
|
"VirtualItemsSelectionRangeChanged"
|
|
});
|
|
|
|
// If we are looking at a TreeListView, remove group related events
|
|
// since TreeListViews can't show groups
|
|
if (this.Control is TreeListView) {
|
|
unwanted.AddRange(new string[] {
|
|
"AboutToCreateGroups",
|
|
"AfterCreatingGroups",
|
|
"BeforeCreatingGroups",
|
|
"GroupTaskClicked",
|
|
"GroupExpandingCollapsing",
|
|
"GroupStateChanged"
|
|
});
|
|
}
|
|
|
|
foreach (string unwantedEvent in unwanted) {
|
|
EventDescriptor eventDesc = TypeDescriptor.CreateEvent(
|
|
typeof(ObjectListView),
|
|
(EventDescriptor)events[unwantedEvent],
|
|
new BrowsableAttribute(false));
|
|
events[unwantedEvent] = eventDesc;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows a designer to change or remove items from the set of attributes that it exposes through a <see cref="T:System.ComponentModel.TypeDescriptor"/>.
|
|
/// </summary>
|
|
/// <param name="attributes">The attributes for the class of the component. </param>
|
|
protected override void PostFilterAttributes(IDictionary attributes) {
|
|
// Debug.WriteLine("ObjectListViewDesigner.PostFilterAttributes");
|
|
this.designerFilter.PostFilterAttributes(attributes);
|
|
base.PostFilterAttributes(attributes);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows a designer to change or remove items from the set of events that it exposes through a <see cref="T:System.ComponentModel.TypeDescriptor"/>.
|
|
/// </summary>
|
|
/// <param name="events">The events for the class of the component. </param>
|
|
protected override void PostFilterEvents(IDictionary events) {
|
|
// Debug.WriteLine("ObjectListViewDesigner.PostFilterEvents");
|
|
this.designerFilter.PostFilterEvents(events);
|
|
base.PostFilterEvents(events);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Overrides
|
|
|
|
/// <summary>
|
|
/// Gets the design-time action lists supported by the component associated with the designer.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// The design-time action lists supported by the component associated with the designer.
|
|
/// </returns>
|
|
public override DesignerActionListCollection ActionLists {
|
|
get {
|
|
// We want to change the first action list so it only has the commands we want
|
|
DesignerActionListCollection actionLists = this.listViewDesigner.ActionLists;
|
|
if (actionLists.Count > 0 && !(actionLists[0] is ListViewActionListAdapter)) {
|
|
actionLists[0] = new ListViewActionListAdapter(this, actionLists[0]);
|
|
}
|
|
return actionLists;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the collection of components associated with the component managed by the designer.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// The components that are associated with the component managed by the designer.
|
|
/// </returns>
|
|
public override ICollection AssociatedComponents {
|
|
get {
|
|
ArrayList components = new ArrayList(base.AssociatedComponents);
|
|
components.AddRange(this.listViewDesigner.AssociatedComponents);
|
|
return components;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates whether a mouse click at the specified point should be handled by the control.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// true if a click at the specified point is to be handled by the control; otherwise, false.
|
|
/// </returns>
|
|
/// <param name="point">A <see cref="T:System.Drawing.Point"/> indicating the position at which the mouse was clicked, in screen coordinates. </param>
|
|
protected override bool GetHitTest(Point point) {
|
|
// The ListViewDesigner wants to allow column dividers to be resized
|
|
return (bool)this.listViewDesignGetHitTest.Invoke(listViewDesigner, new object[] { point });
|
|
}
|
|
|
|
/// <summary>
|
|
/// Processes Windows messages and optionally routes them to the control.
|
|
/// </summary>
|
|
/// <param name="m">The <see cref="T:System.Windows.Forms.Message"/> to process. </param>
|
|
protected override void WndProc(ref Message m) {
|
|
switch (m.Msg) {
|
|
case 0x4e:
|
|
case 0x204e:
|
|
// The listview designer is interested in HDN_ENDTRACK notifications
|
|
this.listViewDesignWndProc.Invoke(listViewDesigner, new object[] { m });
|
|
break;
|
|
default:
|
|
base.WndProc(ref m);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Implementation variables
|
|
|
|
private ControlDesigner listViewDesigner;
|
|
private IDesignerFilter designerFilter;
|
|
private MethodInfo listViewDesignGetHitTest;
|
|
private MethodInfo listViewDesignWndProc;
|
|
|
|
#endregion
|
|
|
|
#region Custom action list
|
|
|
|
/// <summary>
|
|
/// This class modifies a ListViewActionList, by removing the "Edit Items" and "Edit Groups" actions.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// That class is internal, so we cannot simply subclass it, which would be simplier.
|
|
/// </para>
|
|
/// <para>
|
|
/// Action lists use reflection to determine if that action can be executed, so we not
|
|
/// only have to modify the returned collection of actions, but we have to implement
|
|
/// the properties and commands that the returned actions use. </para>
|
|
/// </remarks>
|
|
private class ListViewActionListAdapter : DesignerActionList
|
|
{
|
|
public ListViewActionListAdapter(ObjectListViewDesigner designer, DesignerActionList wrappedList)
|
|
: base(wrappedList.Component) {
|
|
this.designer = designer;
|
|
this.wrappedList = wrappedList;
|
|
}
|
|
|
|
public override DesignerActionItemCollection GetSortedActionItems() {
|
|
DesignerActionItemCollection items = wrappedList.GetSortedActionItems();
|
|
items.RemoveAt(2); // remove Edit Groups
|
|
items.RemoveAt(0); // remove Edit Items
|
|
return items;
|
|
}
|
|
|
|
private void EditValue(ComponentDesigner componentDesigner, IComponent iComponent, string propertyName) {
|
|
// One more complication. The ListViewActionList classes uses an internal class, EditorServiceContext, to
|
|
// edit the items/columns/groups collections. So, we use reflection to bypass the data hiding.
|
|
Type tEditorServiceContext = Type.GetType("System.Windows.Forms.Design.EditorServiceContext, System.Design");
|
|
tEditorServiceContext.InvokeMember("EditValue", BindingFlags.InvokeMethod | BindingFlags.Static, null, null, new object[] { componentDesigner, iComponent, propertyName });
|
|
}
|
|
|
|
private void SetValue(object target, string propertyName, object value) {
|
|
TypeDescriptor.GetProperties(target)[propertyName].SetValue(target, value);
|
|
}
|
|
|
|
public void InvokeColumnsDialog() {
|
|
EditValue(this.designer, base.Component, "Columns");
|
|
}
|
|
|
|
// Don't need these since we removed their corresponding actions from the list.
|
|
// Keep the methods just in case.
|
|
|
|
//public void InvokeGroupsDialog() {
|
|
// EditValue(this.designer, base.Component, "Groups");
|
|
//}
|
|
|
|
//public void InvokeItemsDialog() {
|
|
// EditValue(this.designer, base.Component, "Items");
|
|
//}
|
|
|
|
public ImageList LargeImageList {
|
|
get { return ((ListView)base.Component).LargeImageList; }
|
|
set { SetValue(base.Component, "LargeImageList", value); }
|
|
}
|
|
|
|
public ImageList SmallImageList {
|
|
get { return ((ListView)base.Component).SmallImageList; }
|
|
set { SetValue(base.Component, "SmallImageList", value); }
|
|
}
|
|
|
|
public View View {
|
|
get { return ((ListView)base.Component).View; }
|
|
set { SetValue(base.Component, "View", value); }
|
|
}
|
|
|
|
ObjectListViewDesigner designer;
|
|
DesignerActionList wrappedList;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DesignerCommandSet
|
|
|
|
private class CDDesignerCommandSet : DesignerCommandSet
|
|
{
|
|
|
|
public CDDesignerCommandSet(ComponentDesigner componentDesigner) {
|
|
this.componentDesigner = componentDesigner;
|
|
}
|
|
|
|
public override ICollection GetCommands(string name) {
|
|
// Debug.WriteLine("CDDesignerCommandSet.GetCommands:" + name);
|
|
if (componentDesigner != null) {
|
|
if (name.Equals("Verbs")) {
|
|
return componentDesigner.Verbs;
|
|
}
|
|
if (name.Equals("ActionLists")) {
|
|
return componentDesigner.ActionLists;
|
|
}
|
|
}
|
|
return base.GetCommands(name);
|
|
}
|
|
|
|
private readonly ComponentDesigner componentDesigner;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// This class works in conjunction with the OLVColumns property to allow OLVColumns
|
|
/// to be added to the ObjectListView.
|
|
/// </summary>
|
|
public class OLVColumnCollectionEditor : System.ComponentModel.Design.CollectionEditor
|
|
{
|
|
/// <summary>
|
|
/// Create a OLVColumnCollectionEditor
|
|
/// </summary>
|
|
/// <param name="t"></param>
|
|
public OLVColumnCollectionEditor(Type t)
|
|
: base(t) {
|
|
}
|
|
|
|
/// <summary>
|
|
/// What type of object does this editor create?
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected override Type CreateCollectionItemType() {
|
|
return typeof(OLVColumn);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Edit a given value
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
/// <param name="provider"></param>
|
|
/// <param name="value"></param>
|
|
/// <returns></returns>
|
|
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
|
|
if (context == null)
|
|
throw new ArgumentNullException("context");
|
|
if (provider == null)
|
|
throw new ArgumentNullException("provider");
|
|
|
|
// Figure out which ObjectListView we are working on. This should be the Instance of the context.
|
|
ObjectListView olv = context.Instance as ObjectListView;
|
|
Debug.Assert(olv != null, "Instance must be an ObjectListView");
|
|
|
|
// Edit all the columns, not just the ones that are visible
|
|
base.EditValue(context, provider, olv.AllColumns);
|
|
|
|
// Set the columns on the ListView to just the visible columns
|
|
List<OLVColumn> newColumns = olv.GetFilteredColumns(View.Details);
|
|
olv.Columns.Clear();
|
|
olv.Columns.AddRange(newColumns.ToArray());
|
|
|
|
return olv.Columns;
|
|
}
|
|
|
|
/// <summary>
|
|
/// What text should be shown in the list for the given object?
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
/// <returns></returns>
|
|
protected override string GetDisplayText(object value) {
|
|
OLVColumn col = value as OLVColumn;
|
|
if (col == null || String.IsNullOrEmpty(col.AspectName))
|
|
return base.GetDisplayText(value);
|
|
|
|
return String.Format("{0} ({1})", base.GetDisplayText(value), col.AspectName);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Control how the overlay is presented in the IDE
|
|
/// </summary>
|
|
internal class OverlayConverter : ExpandableObjectConverter
|
|
{
|
|
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
|
|
return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
|
|
}
|
|
|
|
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) {
|
|
if (destinationType == typeof(string)) {
|
|
ImageOverlay imageOverlay = value as ImageOverlay;
|
|
if (imageOverlay != null) {
|
|
return imageOverlay.Image == null ? "(none)" : "(set)";
|
|
}
|
|
TextOverlay textOverlay = value as TextOverlay;
|
|
if (textOverlay != null) {
|
|
return String.IsNullOrEmpty(textOverlay.Text) ? "(none)" : "(set)";
|
|
}
|
|
}
|
|
|
|
return base.ConvertTo(context, culture, value, destinationType);
|
|
}
|
|
}
|
|
}
|