0

Is it possible to exchange two UserControls with databinding in WinForms?

I would like to change the applications UI dependending on which ComboBox item is currently selected. I have bound my ComboBox.SelectedValue to a property and would like to exchange the UserControls now within the setter of that property.

I tried adding a equally sized panel to the form and tried setting the panels DataSource to a BindingList<Control> or something similar, unfortunately a panel doesn't seem to have a DataSource similar to a ComboBox...

I would be glad, if you could give me a small hint on how to databind my UserControls to my form. Thanks in advance.

Jannik
  • 2,310
  • 6
  • 32
  • 61
  • Just make some panels and put them in your form and then make one of them visible based on selected item of your combobox, and set others invisible. It doesn't need such trick of data binding. – Reza Aghaei Oct 06 '15 at 16:42

1 Answers1

0

A little bit harder, but doable. The main problem in WF data binding is the lack of support for binding expressions. However, as soon as the source property provides change notification, it can be solved by using the Binding.Format event with the help of a methods like this:

static void Bind(Control target, string targetProperty, object source, string sourceProperty, Func<object, object> expression)
{
    var binding = new Binding(targetProperty, source, sourceProperty, true, DataSourceUpdateMode.Never);
    binding.Format += (sender, e) => e.Value = expression(e.Value);
    target.DataBindings.Add(binding);
}

Sample usage similar to your case:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace Tests
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var form = new Form();
            var topPanel = new Panel { Dock = DockStyle.Top, Parent = form };
            var combo = new ComboBox { Left = 8, Top = 8, Parent = topPanel };
            topPanel.Height = combo.Height + 16;
            combo.Items.AddRange(new[] { "One", "Two" });
            combo.SelectedIndex = 0;
            var panel1 = new Panel { Dock = DockStyle.Fill, Parent = form, BackColor = Color.Red };
            var panel2 = new Panel { Dock = DockStyle.Fill, Parent = form, BackColor = Color.Green };
            Bind(panel1, "Visible", combo, "SelectedIndex", value => (int)value == 0);
            Bind(panel2, "Visible", combo, "SelectedIndex", value => (int)value == 1);
            Application.Run(form);
        }
        static void Bind(Control target, string targetProperty, object source, string sourceProperty, Func<object, object> expression)
        {
            var binding = new Binding(targetProperty, source, sourceProperty, true, DataSourceUpdateMode.Never);
            binding.Format += (sender, e) => e.Value = expression(e.Value);
            target.DataBindings.Add(binding);
        }
    }
}
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • Hey Ivan, thanks for trying to help. It is a good hint, I can use Format for the condition (similar to the converter in WPF). I understand whats going on there, but I think, I will stick to my Setter, well, I will see. But here's another question: Are the wrapping Panels necessary then? Can't I just bind the Visible-Property of the usercontrols? (well, if they do have one) – Jannik Oct 07 '15 at 05:20
  • @Jannik: This is just an example. The key point is to use `control.DataBindings` and `Binding`. The `source` does not need to be another control, could be any object having the `targetProperty`, i.e. if you already have a property, you can bind to it. And yes, you can bind Visible, Enabled etc. property of any control - as you can see, the `Bind` method above target is declared as `Control`, not `Panel`, so `UserControl` can be used w/o problem. – Ivan Stoev Oct 07 '15 at 05:39