I am sure this question comes up a lot. I need to disable the controls
on my Windows forms so that when the BindingSource is empty some the
controls bound to it will be disabled.
This will make it clear to the user that they have to create a new
item first before they start working.
This needs to be an easy process because there are many, many forms.
We can't just go through every control and disable it since some
controls need to always be enabled. My hope was to perform the
operation with some sort of binding. [...]
You don't have enough information in your question for us to understand
exactly what you're doing. For example, what kind of control are you
using? What kind of data source is bound to the control? Is it always an
instance of BindingSource? Or do you bind with other types as well?
Generally speaking, I'm not aware of any particular feature in data
binding that does exactly that. But you can easily implement what you're
asking using the appropriate events on the objects you're dealing with.
In particular, when you create the controls, subscribe a handler to the
DataSourceChanged event. In that event, depending on the type of the data
source, subscribe a handler to the appropriate event (e.g. ListChanged)
that will enable/disable the control as appropriate for the data.
For example, let's assuming we're binding a BindingSource instance to a
DataGridView control. The above might look something like this:
class Form1 : Form
{
public Form1()
{
InitializeComponent();
dataGridView1.DataSourceChanged +=
dataGridView1_DataSourceChanged;
}
private void dataGridView1_DataSourceChanged(object sender,
EventArgs e)
{
DataGridView dgv = (DataGridView)sender;
if (dgv.DataSource != null)
{
BindingSource source = (BindingSource)dgv.DataSource;
source.ListChanged += delegate(object sender,
ListChangedEventArgs e)
{
BindingSource source = (BindingSource)sender;
dgv.Enabled = source.Count >0;
}
}
}
}
The above assumes that if the binding source for a control is changed, the
source itself is discarded, never used again. If that assumption isn't
true, you'll have to include some code to unsubscribe the previous
BindingSource.ListChanged event handler, to ensure that the control
doesn't wind up getting enabled or disabled spuriously (or worse, after
it's been disposed). This is kind of a pain, because you'd have to keep a
reference to the previous DataSource, but it's doable and necessary in
that situation. (It's unfortunate that the "XXXChanged" events in .NET
don't include an easy way to get at the old value, but that's what we're
stuck with).
Pete
Thanks, Pete. That was informative. I kind of took your idea and made
an extender control. However, the dang thing doesn't work, and I can't
figure out why. If this topic is still alive, could you please look
and see. I'd appreciate it.
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using PowerBillingUI.Controls;
namespace PowerBillingUI.Helpers
{
[ProvideProperty("DisableWhenUnbound", typeof(Control))]
public class DisableHelper : Component, IExtenderProvider
{
private class Handlers
{
public ListChangedEventHandler ListChangedHandler;
public EventHandler EnableChangedHandler;
}
private IndirectBindingSource bindingSource; // just think of
this as a BindingSource
private IDictionary<Control, Handlers> listeners;
private bool enabled;
public DisableHelper()
{
listeners = new Dictionary<Control, Handlers>();
}
public bool Enabled
{
get
{
return enabled;
}
set
{
if (bindingSource != null && enabled)
{
foreach (KeyValuePair<Control, Handlers> pair in
listeners)
{
bindingSource.ListChanged -=
pair.Value.ListChangedHandler;
pair.Key.EnabledChanged -=
pair.Value.EnableChangedHandler;
}
}
enabled = value;
if (enabled && bindingSource != null)
{
foreach (KeyValuePair<Control, Handlers> pair in
listeners)
{
bindingSource.ListChanged +=
pair.Value.ListChangedHandler;
pair.Key.EnabledChanged +=
pair.Value.EnableChangedHandler;
}
}
}
}
public IndirectBindingSource BindingSource
{
get
{
return bindingSource;
}
set
{
bindingSource = value;
}
}
public bool CanExtend(object extendee)
{
return extendee is Control;
}
[DefaultValue(false)]
public bool GetDisableWhenUnbound(Control control)
{
return listeners.ContainsKey(control);
}
public void SetDisableWhenUnbound(Control control, bool
disableWhenUnbound)
{
Handlers handlers;
if (!listeners.TryGetValue(control, out handlers))
{
handlers = null;
}
if (handlers != null)
{
if (bindingSource != null)
{
bindingSource.ListChanged -=
handlers.ListChangedHandler;
}
control.EnabledChanged -=
handlers.EnableChangedHandler;
handlers = null;
}
if (disableWhenUnbound)
{
handlers = new Handlers();
handlers.EnableChangedHandler = (EventHandler)
delegate(object sender, EventArgs e)
{
control.Enabled = bindingSource.Count > 0;
};
handlers.ListChangedHandler =
(ListChangedEventHandler)
delegate(object sender, ListChangedEventArgs e)
{
control.Enabled = !control.Enabled; // fires
EnableChangedHandler
};
}
if (handlers == null)
{
listeners.Remove(control);
}
else
{
listeners[control] = handlers;
}
}
}
}