Replacing the Visual Studio Select Resource Dialog Box
In my project I have over 750 images in a resource. Using VS built into the Select Resource dialog is a nightmare to find and select a single image - let them say - per button in the winforms designer.
It would be much more convenient if it was some kind of explorer-like dialog and that lack of search functionality.
-
Do you have an idea to replace this dialogue?
-
Is there any extension that can do this?
-
If there is no such extension, I would create an extension / add-on, which is what I need to do. Do you have any real experience, if it can be done at all?
-
I thought I would find a suitable DLL and extend it beaviour but unfortunately I cannot find which DLL contains this tragedy
Any help would be appreciated, thanks!
source to share
Resource selection dialog box - UITypeEditor
. It is an inner class ResourceEditorSwitch<T>
that uses an inner class internally ResourcePickerDialog
, and both are in an assembly Microsoft.VisualStudio.Windows.Forms.dll
, which is one of the assemblies of a Visual Studio assembly.
Since the implementation of the class is closely related to some of the other internal classes of the Visual Studio compilations, so it is difficult to extract the source code and customize it, but you have such information about the class that will help us take a look in its source code and give us more information about the class.
To customize the resource selection dialog. Instead, you can get an instance of the class at design time, and before displaying the dialog, process the dialog using code to have a filtering function like the following gif, note TextBox
that I'm 'added to the dialog:
You can filter ListBox
by typing TextBox
and using keys โand โwithout changing focus from TextBox
, you can select filtered results.
To do this, you need:
- Create
ControlDesigner
and register it as the constructor for your control. Then in,OnCreateHandle
find the property you are going to edit. For exampleBackgroundImage
. - Find
UITypeEditor
this property. The editor is of the typeResourceEditorSwitch<T>
that the instance usesResourcePickerDialog
. Get a copy forResourcePickerDialog
. -
Get the field
resourcePickerUI
and instantiate the dialogresourcePickerUI
. This is a dialogue that you must change. The dialog contains severalTableLayoutPanel
. You have to pasteTextBox
in a suitable place and handle the eventTextChanged
and filter the values โโthat are displayed inListBox
. All controls are named and you can simply access them and change their properties and values. -
After changing the shape, assign it
resourcePickerUI
. This way the editor will use the modified form and show you what you need.
Implementation
You can find a complete working example in the following repository:
Here's the code for the designer:
public class MyControlDesigner : ControlDesigner
{
protected override void OnCreateHandle()
{
base.OnCreateHandle();
var property = TypeDescriptor.GetProperties(this.Control)["BackgroundImage"];
var resourceEditorSwitch = property.GetEditor(typeof(UITypeEditor)) as UITypeEditor;
var editorToUseField = resourceEditorSwitch.GetType().GetProperty("EditorToUse",
BindingFlags.NonPublic | BindingFlags.Instance);
var editorToUse = editorToUseField.GetValue(resourceEditorSwitch);
var resourcePickerUIField = editorToUse.GetType().GetField("resourcePickerUI",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic);
var resourcePickerUI = (Form)Activator.CreateInstance(resourcePickerUIField.FieldType);
ModifyForm(resourcePickerUI);
resourcePickerUIField.SetValue(editorToUse, resourcePickerUI);
}
void ModifyForm(Form f)
{
var resourceContextTableLayoutPanel = GetControl<TableLayoutPanel>(f, "resourceContextTableLayoutPanel");
var resourceList = GetControl<ListBox>(f, "resourceList");
resourceContextTableLayoutPanel.Controls.Remove(resourceList);
var tableLayoutPanel = new TableLayoutPanel();
tableLayoutPanel.Dock = DockStyle.Fill;
tableLayoutPanel.Margin = new Padding(0);
tableLayoutPanel.ColumnCount = 1;
tableLayoutPanel.RowCount = 2;
tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
List<string> list = new List<string>();
var textBox = new TextBox() { Dock = DockStyle.Fill, Margin = resourceList.Margin };
Action<string> applyFilter = (s) =>
{
if (string.IsNullOrEmpty(s))
{
resourceList.BeginUpdate();
resourceList.Items.Clear();
resourceList.Items.AddRange(list.ToArray());
resourceList.EndUpdate();
}
else
{
var list2 = list.Where(x => x.ToLower().StartsWith(s.ToLower())).ToList();
resourceList.BeginUpdate();
resourceList.Items.Clear();
resourceList.Items.Add("(none)");
resourceList.Items.AddRange(list2.ToArray());
resourceList.EndUpdate();
}
if (resourceList.Items.Count > 1)
resourceList.SelectedIndex = 1;
else
resourceList.SelectedIndex = 0;
};
var resxCombo = GetControl<ComboBox>(f, "resxCombo");
resxCombo.SelectedValueChanged += (s, e) =>
{
resxCombo.BeginInvoke(new Action(() =>
{
if (resourceList.Items.Count > 0)
{
list = resourceList.Items.Cast<string>().ToList();
textBox.Text = string.Empty;
}
}));
};
textBox.TextChanged += (s, e) => applyFilter(textBox.Text);
textBox.KeyDown += (s, e) =>
{
if (e.KeyCode == Keys.Up)
{
e.Handled = true;
if (resourceList.SelectedIndex >= 1)
resourceList.SelectedIndex--;
}
if (e.KeyCode == Keys.Down)
{
e.Handled = true;
if (resourceList.SelectedIndex < resourceList.Items.Count - 1)
resourceList.SelectedIndex++;
}
};
tableLayoutPanel.Controls.Add(textBox, 0, 0);
resourceList.EnabledChanged += (s, e) =>
{
textBox.Enabled = resourceList.Enabled;
};
tableLayoutPanel.Controls.Add(resourceList, 0, 1);
resourceContextTableLayoutPanel.Controls.Add(tableLayoutPanel, 0, 4);
}
T GetControl<T>(Control c, string name)
where T : Control
{
return (T)c.Controls.Find(name, true).FirstOrDefault();
}
}
source to share