|
//------------------------------------------------------------------------------
// <copyright file="ListControl.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Windows.Forms {
using System.Runtime.Serialization.Formatters;
using System.Runtime.Remoting;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System;
using System.Security.Permissions;
using System.Globalization;
using System.Windows.Forms;
using System.Drawing.Design;
using System.ComponentModel;
using System.Windows.Forms.ComponentModel;
using System.Collections;
using System.Drawing;
using Microsoft.Win32;
using System.Text;
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
ComVisible(true),
ClassInterface(ClassInterfaceType.AutoDispatch),
LookupBindingProperties("DataSource", "DisplayMember", "ValueMember", "SelectedValue")
]
public abstract class ListControl : Control {
private static readonly object EVENT_DATASOURCECHANGED = new object();
private static readonly object EVENT_DISPLAYMEMBERCHANGED = new object();
private static readonly object EVENT_VALUEMEMBERCHANGED = new object();
private static readonly object EVENT_SELECTEDVALUECHANGED = new object();
private static readonly object EVENT_FORMATINFOCHANGED = new object();
private static readonly object EVENT_FORMATSTRINGCHANGED = new object();
private static readonly object EVENT_FORMATTINGENABLEDCHANGED = new object();
private object dataSource;
private CurrencyManager dataManager;
private BindingMemberInfo displayMember;
private BindingMemberInfo valueMember;
// Formatting stuff
private string formatString = String.Empty;
private IFormatProvider formatInfo = null;
private bool formattingEnabled = false;
private static readonly object EVENT_FORMAT = new object();
private TypeConverter displayMemberConverter = null;
private static TypeConverter stringTypeConverter = null;
private bool isDataSourceInitialized;
private bool isDataSourceInitEventHooked;
private bool inSetDataConnection = false;
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.DataSource"]/*' />
/// <devdoc>
/// The ListSource to consume as this ListBox's source of data.
/// When set, a user can not modify the Items collection.
/// </devdoc>
[
SRCategory(SR.CatData),
DefaultValue(null),
RefreshProperties(RefreshProperties.Repaint),
AttributeProvider(typeof(IListSource)),
SRDescription(SR.ListControlDataSourceDescr)
]
public object DataSource {
get {
return dataSource;
}
set {
if (value != null && !(value is IList || value is IListSource))
throw new ArgumentException(SR.GetString(SR.BadDataSourceForComplexBinding));
if (dataSource == value)
return;
// When we change the dataSource to null, we should reset
// the displayMember to "". See ASURT 85662.
try {
SetDataConnection(value, displayMember, false);
} catch {
// There are several possibilities why setting the data source throws an exception:
// 1. the app throws an exception in the events that fire when we change the data source: DataSourceChanged,
// 2. we get an exception when we set the data source and populate the list controls (say,something went wrong while formatting the data)
// 3. the DisplayMember does not fit w/ the new data source (this could happen if the user resets the data source but did not reset the DisplayMember)
// in all cases ListControl should reset the DisplayMember to String.Empty
// the ListControl should also eat the exception - this is the RTM behavior and doing anything else is a breaking change
DisplayMember = "";
}
if (value == null)
DisplayMember = "";
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.DataSourceChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListControlOnDataSourceChangedDescr)]
public event EventHandler DataSourceChanged {
add {
Events.AddHandler(EVENT_DATASOURCECHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_DATASOURCECHANGED, value);
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.DataManager"]/*' />
protected CurrencyManager DataManager {
get {
return this.dataManager;
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.DisplayMember"]/*' />
/// <devdoc>
/// If the ListBox contains objects that support properties, this indicates
/// which property of the object to show. If "", the object shows it's ToString().
/// </devdoc>
[
SRCategory(SR.CatData),
DefaultValue(""),
TypeConverterAttribute("System.Windows.Forms.Design.DataMemberFieldConverter, " + AssemblyRef.SystemDesign),
Editor("System.Windows.Forms.Design.DataMemberFieldEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)),
SRDescription(SR.ListControlDisplayMemberDescr)
]
public string DisplayMember {
get {
return displayMember.BindingMember;
}
set {
BindingMemberInfo oldDisplayMember = displayMember;
try {
SetDataConnection(dataSource, new BindingMemberInfo(value), false);
} catch {
displayMember = oldDisplayMember;
}
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.DisplayMemberChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListControlOnDisplayMemberChangedDescr)]
public event EventHandler DisplayMemberChanged {
add {
Events.AddHandler(EVENT_DISPLAYMEMBERCHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_DISPLAYMEMBERCHANGED, value);
}
}
// Cached type converter of the property associated with the display member
private TypeConverter DisplayMemberConverter {
get {
if (this.displayMemberConverter == null &&
this.DataManager != null &&
this.displayMember != null) {
PropertyDescriptorCollection props = this.DataManager.GetItemProperties();
if (props != null) {
PropertyDescriptor displayMemberProperty = props.Find(this.displayMember.BindingField, true);
if (displayMemberProperty != null)
{
this.displayMemberConverter = displayMemberProperty.Converter;
}
}
}
return this.displayMemberConverter;
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.Format"]/*' />
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListControlFormatDescr)]
public event ListControlConvertEventHandler Format
{
add {
Events.AddHandler(EVENT_FORMAT, value);
RefreshItems();
}
remove {
Events.RemoveHandler(EVENT_FORMAT, value);
RefreshItems();
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.FormatInfo"]/*' />
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Advanced),
DefaultValue(null)
]
public IFormatProvider FormatInfo {
get {
return this.formatInfo;
}
set {
if (value != formatInfo) {
formatInfo = value;
RefreshItems();
OnFormatInfoChanged(EventArgs.Empty);
}
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.FormatInfoChanged"]/*' />
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Advanced),
SRCategory(SR.CatPropertyChanged),
SRDescription(SR.ListControlFormatInfoChangedDescr)
]
public event EventHandler FormatInfoChanged {
add {
Events.AddHandler(EVENT_FORMATINFOCHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_FORMATINFOCHANGED, value);
}
}
[
DefaultValue(""),
SRDescription(SR.ListControlFormatStringDescr),
EditorAttribute("System.Windows.Forms.Design.FormatStringEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)),
MergableProperty(false)
]
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.FormatString"]/*' />
public string FormatString {
get {
return formatString;
}
set {
if (value == null)
value = String.Empty;
if (!value.Equals(formatString)) {
formatString = value;
RefreshItems();
OnFormatStringChanged(EventArgs.Empty);
}
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.FormatStringChanged"]/*' />
[
SRCategory(SR.CatPropertyChanged),
SRDescription(SR.ListControlFormatStringChangedDescr)
]
public event EventHandler FormatStringChanged {
add {
Events.AddHandler(EVENT_FORMATSTRINGCHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_FORMATSTRINGCHANGED, value);
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.FormattingEnabled"]/*' />
[
DefaultValue(false),
SRDescription(SR.ListControlFormattingEnabledDescr)
]
public bool FormattingEnabled {
get {
return formattingEnabled;
}
set {
if (value != formattingEnabled) {
formattingEnabled = value;
RefreshItems();
OnFormattingEnabledChanged(EventArgs.Empty);
}
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.FormattingEnabledChanged"]/*' />
[
SRCategory(SR.CatPropertyChanged),
SRDescription(SR.ListControlFormattingEnabledChangedDescr)
]
public event EventHandler FormattingEnabledChanged {
add {
Events.AddHandler(EVENT_FORMATTINGENABLEDCHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_FORMATTINGENABLEDCHANGED, value);
}
}
private bool BindingMemberInfoInDataManager(BindingMemberInfo bindingMemberInfo) {
if (dataManager == null)
return false;
PropertyDescriptorCollection props = dataManager.GetItemProperties();
int propsCount = props.Count;
for (int i = 0; i < propsCount; i++) {
if (typeof(IList).IsAssignableFrom(props[i].PropertyType))
continue;
if (props[i].Name.Equals(bindingMemberInfo.BindingField)) {
return true;
}
}
for (int i = 0; i < propsCount; i++) {
if (typeof(IList).IsAssignableFrom(props[i].PropertyType))
continue;
if (String.Compare(props[i].Name, bindingMemberInfo.BindingField, true, CultureInfo.CurrentCulture) == 0) {
return true;
}
}
return false;
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.ValueMember"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
SRCategory(SR.CatData),
DefaultValue(""),
Editor("System.Windows.Forms.Design.DataMemberFieldEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)),
SRDescription(SR.ListControlValueMemberDescr)
]
public string ValueMember {
get {
return valueMember.BindingMember;
}
set {
if (value == null)
value = "";
BindingMemberInfo newValueMember = new BindingMemberInfo(value);
BindingMemberInfo oldValueMember = valueMember;
if (!newValueMember.Equals(valueMember)) {
// If the displayMember is set to the EmptyString, then recreate the dataConnection
//
if (DisplayMember.Length == 0)
SetDataConnection(DataSource, newValueMember, false);
// See if the valueMember is a member of
// the properties in the dataManager
if (this.dataManager != null && value != null && value.Length != 0)
if (!BindingMemberInfoInDataManager(newValueMember)) {
throw new ArgumentException(SR.GetString(SR.ListControlWrongValueMember), "value");
}
valueMember = newValueMember;
OnValueMemberChanged(EventArgs.Empty);
OnSelectedValueChanged(EventArgs.Empty);
}
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.ValueMemberChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListControlOnValueMemberChangedDescr)]
public event EventHandler ValueMemberChanged {
add {
Events.AddHandler(EVENT_VALUEMEMBERCHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_VALUEMEMBERCHANGED, value);
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.AllowSelection"]/*' />
/// <devdoc>
/// Indicates whether list currently allows selection of list items.
/// </devdoc>
protected virtual bool AllowSelection {
get {
return true;
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.SelectedIndex"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public abstract int SelectedIndex {
get;
set;
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.SelectedValue"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
SRCategory(SR.CatData),
DefaultValue(null),
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.ListControlSelectedValueDescr),
Bindable(true)
]
public object SelectedValue {
get {
if (SelectedIndex != -1 && dataManager != null ) {
object currentItem = dataManager[SelectedIndex];
object filteredItem = FilterItemOnProperty(currentItem, valueMember.BindingField);
return filteredItem;
}
return null;
}
set {
if (dataManager != null) {
string propertyName = valueMember.BindingField;
// we can't set the SelectedValue property when the listManager does not
// have a ValueMember set.
if (string.IsNullOrEmpty(propertyName))
throw new InvalidOperationException(SR.GetString(SR.ListControlEmptyValueMemberInSettingSelectedValue));
PropertyDescriptorCollection props = dataManager.GetItemProperties();
PropertyDescriptor property = props.Find(propertyName, true);
int index = dataManager.Find(property, value, true);
this.SelectedIndex = index;
}
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.SelectedValueChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.ListControlOnSelectedValueChangedDescr)]
public event EventHandler SelectedValueChanged {
add {
Events.AddHandler(EVENT_SELECTEDVALUECHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_SELECTEDVALUECHANGED, value);
}
}
private void DataManager_PositionChanged(object sender, EventArgs e) {
if (this.dataManager != null) {
if (AllowSelection) {
this.SelectedIndex = dataManager.Position;
}
}
}
private void DataManager_ItemChanged(object sender, ItemChangedEventArgs e) {
// Note this is being called internally with a null event.
if (dataManager != null) {
if (e.Index == -1) {
SetItemsCore(dataManager.List);
if (AllowSelection) {
this.SelectedIndex = this.dataManager.Position;
}
}
else {
SetItemCore(e.Index, dataManager[e.Index]);
}
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.FilterItemOnProperty"]/*' />
/// <internalonly/>
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected object FilterItemOnProperty(object item) {
return FilterItemOnProperty(item, displayMember.BindingField);
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.FilterItemOnProperty1"]/*' />
/// <internalonly/>
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected object FilterItemOnProperty(object item, string field) {
if (item != null && field.Length > 0) {
try {
// if we have a dataSource, then use that to display the string
PropertyDescriptor descriptor;
if (this.dataManager != null)
descriptor = this.dataManager.GetItemProperties().Find(field, true);
else
descriptor = TypeDescriptor.GetProperties(item).Find(field, true);
if (descriptor != null) {
item = descriptor.GetValue(item);
}
}
catch {
}
}
return item;
}
//We use this to prevent getting the selected item when mouse is hovering over the dropdown.
//
internal bool BindingFieldEmpty {
get {
return (displayMember.BindingField.Length > 0 ? false : true);
}
}
internal int FindStringInternal(string str, IList items, int startIndex, bool exact) {
return FindStringInternal(str, items, startIndex, exact, true);
}
internal int FindStringInternal(string str, IList items, int startIndex, bool exact, bool ignorecase) {
// Sanity check parameters
//
if (str == null || items == null) {
return -1;
}
// VSWhidbey 95158: The last item in the list is still a valid place to start looking!
if (startIndex < -1 || startIndex >= items.Count) {
return -1;
}
bool found = false;
int length = str.Length;
// VSWhidbey 215677: start from the start index and wrap around until we find the string
// in question. Use a separate counter to ensure that we arent cycling through the list infinitely.
int numberOfTimesThroughLoop = 0;
// this API is really Find NEXT String...
for (int index = (startIndex+1) % items.Count; numberOfTimesThroughLoop < items.Count; index = (index+1) % items.Count) {
numberOfTimesThroughLoop++;
if (exact) {
found = String.Compare(str, GetItemText(items[index]), ignorecase, CultureInfo.CurrentCulture) == 0;
}
else {
found = String.Compare(str, 0, GetItemText(items[index]), 0, length, ignorecase, CultureInfo.CurrentCulture) == 0;
}
if (found) {
return index;
}
}
return -1;
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.GetItemText"]/*' />
public string GetItemText(object item)
{
// !formattingEnabled == RTM behaviour
if (!formattingEnabled) {
// Microsoft gave his blessing to this RTM breaking change
if (item == null) {
return String.Empty;
}
item = FilterItemOnProperty(item, displayMember.BindingField);
return (item != null) ? Convert.ToString(item, CultureInfo.CurrentCulture) : "";
}
//
// Whidbey formatting features
//
object filteredItem = FilterItemOnProperty(item, displayMember.BindingField);
// first try: the OnFormat event
ListControlConvertEventArgs e = new ListControlConvertEventArgs(filteredItem, typeof(String), item);
OnFormat(e);
// Microsoft: we need a better check. Should add the Handled property on the ListControlConvertEventArgs?
if (e.Value != item && e.Value is String) {
return (string) e.Value;
}
// try Formatter::FormatObject
if (stringTypeConverter == null)
{
stringTypeConverter = TypeDescriptor.GetConverter(typeof(String));
}
try
{
return (string) Formatter.FormatObject(filteredItem, typeof(String), this.DisplayMemberConverter, stringTypeConverter, formatString, formatInfo, null, System.DBNull.Value);
}
catch (Exception exception)
{
if (ClientUtils.IsSecurityOrCriticalException(exception))
{
throw;
}
// if we did not do any work then return the old ItemText
return (filteredItem != null) ? Convert.ToString(item, CultureInfo.CurrentCulture) : "";
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.IsInputKey"]/*' />
/// <devdoc>
/// Handling special input keys, such as pgup, pgdown, home, end, etc...
/// </devdoc>
protected override bool IsInputKey(Keys keyData) {
if ((keyData & Keys.Alt) == Keys.Alt) return false;
switch (keyData & Keys.KeyCode) {
case Keys.PageUp:
case Keys.PageDown:
case Keys.Home:
case Keys.End:
return true;
}
return base.IsInputKey(keyData);
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.OnBindingContextChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override void OnBindingContextChanged(EventArgs e) {
SetDataConnection(dataSource, displayMember, true);
base.OnBindingContextChanged(e);
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.OnDataSourceChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnDataSourceChanged(EventArgs e) {
EventHandler eh = Events[EVENT_DATASOURCECHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.OnDisplayMemberChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnDisplayMemberChanged(EventArgs e) {
EventHandler eh = Events[EVENT_DISPLAYMEMBERCHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.OnFormat"]/*' />
protected virtual void OnFormat(ListControlConvertEventArgs e) {
ListControlConvertEventHandler eh = Events[EVENT_FORMAT] as ListControlConvertEventHandler;
if (eh != null)
eh(this, e);
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.OnFormatInfoChanged"]/*' />
protected virtual void OnFormatInfoChanged(EventArgs e) {
EventHandler eh = Events[EVENT_FORMATINFOCHANGED] as EventHandler;
if (eh != null)
eh(this,e);
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.OnFormatStringChanged"]/*' />
protected virtual void OnFormatStringChanged(EventArgs e) {
EventHandler eh = Events[EVENT_FORMATSTRINGCHANGED] as EventHandler;
if (eh != null)
eh(this,e);
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.OnFormattingEnabledChanged"]/*' />
protected virtual void OnFormattingEnabledChanged(EventArgs e) {
EventHandler eh = Events[EVENT_FORMATTINGENABLEDCHANGED] as EventHandler;
if (eh != null)
eh(this,e);
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.OnSelectedIndexChanged"]/*' />
/// <devdoc>
/// Actually goes and fires the selectedIndexChanged event. Inheriting controls
/// should use this to know when the event is fired [this is preferable to
/// adding an event handler on yourself for this event]. They should,
/// however, remember to call base.OnSelectedIndexChanged(e); to ensure the event is
/// still fired to external listeners
/// </devdoc>
protected virtual void OnSelectedIndexChanged(EventArgs e) {
OnSelectedValueChanged(EventArgs.Empty);
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.OnValueMemberChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnValueMemberChanged(EventArgs e) {
EventHandler eh = Events[EVENT_VALUEMEMBERCHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.OnSelectedValueChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnSelectedValueChanged(EventArgs e) {
EventHandler eh = Events[EVENT_SELECTEDVALUECHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.RefreshItem"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected abstract void RefreshItem(int index);
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.RefreshItems"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void RefreshItems() {
}
private void DataSourceDisposed(object sender, EventArgs e) {
Debug.Assert(sender == this.dataSource, "how can we get dispose notification for anything other than our dataSource?");
SetDataConnection(null, new BindingMemberInfo(""), true);
}
private void DataSourceInitialized(object sender, EventArgs e) {
ISupportInitializeNotification dsInit = (this.dataSource as ISupportInitializeNotification);
Debug.Assert(dsInit != null, "ListControl: ISupportInitializeNotification.Initialized event received, but current DataSource does not support ISupportInitializeNotification!");
Debug.Assert(dsInit.IsInitialized, "ListControl: DataSource sent ISupportInitializeNotification.Initialized event but before it had finished initializing.");
SetDataConnection(this.dataSource, this.displayMember, true);
}
private void SetDataConnection(object newDataSource, BindingMemberInfo newDisplayMember, bool force) {
bool dataSourceChanged = dataSource != newDataSource;
bool displayMemberChanged = !displayMember.Equals(newDisplayMember);
// make sure something interesting is happening...
//
//force = force && (dataSource != null || newDataSource != null);
if (inSetDataConnection) {
return;
}
try {
if (force || dataSourceChanged || displayMemberChanged) {
inSetDataConnection = true;
IList currentList = this.DataManager != null ? this.DataManager.List : null;
bool currentManagerIsNull = this.DataManager == null;
UnwireDataSource();
dataSource = newDataSource;
displayMember = newDisplayMember;
WireDataSource();
// Provided the data source has been fully initialized, start listening to change events on its
// currency manager and refresh our list. If the data source has not yet been initialized, we will
// skip this step for now, and try again later (once the data source has fired its Initialized event).
//
if (isDataSourceInitialized) {
CurrencyManager newDataManager = null;
if (newDataSource != null && BindingContext != null && !(newDataSource == Convert.DBNull)) {
newDataManager = (CurrencyManager)BindingContext[newDataSource, newDisplayMember.BindingPath];
}
if (dataManager != newDataManager) {
if (dataManager != null) {
dataManager.ItemChanged -= new ItemChangedEventHandler(DataManager_ItemChanged);
dataManager.PositionChanged -= new EventHandler(DataManager_PositionChanged);
}
dataManager = newDataManager;
if (dataManager != null) {
dataManager.ItemChanged += new ItemChangedEventHandler(DataManager_ItemChanged);
dataManager.PositionChanged += new EventHandler(DataManager_PositionChanged);
}
}
// See if the BindingField in the newDisplayMember is valid
// The same thing if dataSource Changed
// "" is a good value for displayMember
if (dataManager != null && (displayMemberChanged || dataSourceChanged) && displayMember.BindingMember != null && displayMember.BindingMember.Length != 0) {
if (!BindingMemberInfoInDataManager(displayMember))
throw new ArgumentException(SR.GetString(SR.ListControlWrongDisplayMember), "newDisplayMember");
}
if (dataManager != null && (dataSourceChanged || displayMemberChanged || force)) {
// if we force a new data manager, then change the items in the list control
// only if the list changed or if we go from a null dataManager to a full fledged one
// or if the DisplayMember changed
if (displayMemberChanged || (force && (currentList != this.dataManager.List || currentManagerIsNull))) {
DataManager_ItemChanged(dataManager, new ItemChangedEventArgs(-1));
}
}
}
this.displayMemberConverter = null;
}
if (dataSourceChanged) {
OnDataSourceChanged(EventArgs.Empty);
}
if (displayMemberChanged) {
OnDisplayMemberChanged(EventArgs.Empty);
}
}
finally {
inSetDataConnection = false;
}
}
private void UnwireDataSource() {
// If the source is a component, then unhook the Disposed event
if (this.dataSource is IComponent) {
((IComponent) this.dataSource).Disposed -= new EventHandler(DataSourceDisposed);
}
ISupportInitializeNotification dsInit = (this.dataSource as ISupportInitializeNotification);
if (dsInit != null && isDataSourceInitEventHooked) {
// If we previously hooked the data source's ISupportInitializeNotification
// Initialized event, then unhook it now (we don't always hook this event,
// only if we needed to because the data source was previously uninitialized)
dsInit.Initialized -= new EventHandler(DataSourceInitialized);
isDataSourceInitEventHooked = false;
}
}
private void WireDataSource() {
// If the source is a component, then hook the Disposed event,
// so we know when the component is deleted from the form
if (this.dataSource is IComponent) {
((IComponent) this.dataSource).Disposed += new EventHandler(DataSourceDisposed);
}
ISupportInitializeNotification dsInit = (this.dataSource as ISupportInitializeNotification);
if (dsInit != null && !dsInit.IsInitialized) {
// If the source provides initialization notification, and is not yet
// fully initialized, then hook the Initialized event, so that we can
// delay connecting to it until it *is* initialized.
dsInit.Initialized += new EventHandler(DataSourceInitialized);
isDataSourceInitEventHooked = true;
isDataSourceInitialized = false;
}
else {
// Otherwise either the data source says it *is* initialized, or it
// does not support the capability to report whether its initialized,
// in which case we have to just assume it that is initialized.
isDataSourceInitialized = true;
}
}
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.SetItemsCore"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected abstract void SetItemsCore(IList items);
/// <include file='doc\ListControl.uex' path='docs/doc[@for="ListControl.SetItemCore"]/*' />
protected virtual void SetItemCore(int index, object value) {}
}
}
|