Задача:
Необходимо реализвать приложение загружающее формы в соответствии с правами пользователя. Или, говоря простым языком, каждый пользователь после аутентификации должен получать главное меню ориентированное под его задачи. Ингода формы связанные с пунктами меню должны быть сразу загружены.
Средства достижения:
1. Reflection - используется для динамического создания объектов
2. Интерфейc IFactoryForm - для того чтобы мы спросили у созданной формы, нужен ли ей пункт меню, а также получили возможность передавать форме команды из главного приложения, а также получать команды из формы, для передачи в другие формы (во завернул) .
3. Enum содержащий все команды - для организации взаимодействия форм между собой в процессе работы.
4. Интерфейс IParam для передачи параметров команды. Кстати этот интерфейс ничего не содержит и создан только для того, чтобы при передаче параметорв команды разработчик работающий с фабрикой понимал, что он передает не просто объект, а объект параметр команды.
5. Ну и собственно сама фабрика - часть кода выполняющая работу с загружаемымми формами.
Давайте начнем в таком порядке и обсуждать.
1. Reflection - написано очень много всего хорошего, поэтому здесь я останавливаться не буду. Кто с Reflection не работал может посмотреть в msdn.
2. Как я уже сказал интерфейс используется для взаимодействия фабрики с формой. Для ростоты использования реализовывать лучше в отдельной dll. В самом простом случае должен иметь вид:
Код интерфейса будет иметь вид:
public interface IFactoryForm
{
event CommandHandler NewCommand;
/// <summary>
/// Возвращает с каким пунктом меню необходимо связать форму.
/// </summary>
/// <returns>
/// Путь до пункта меню добавляется форма. Путь имеет вид:
/// Item->Item->Item
/// Если строка пустая, то пункт меню создавать не надо и форма из пункта меню не вызывается
/// </returns>
string GetMenuItem();
/// <summary>
/// Вызывается при клике пользователя на пункте меню связанном с формой
/// </summary>
void Activate(object sender, EventArgs e);
/// <summary>
/// Вызывается главной формой приложения для передачи команд в форму
/// </summary>
void DoCommand(Commands p_command, IParam p_param);
}
* This source code was highlighted with Source Code Highlighter.
3. Перечисление Commands (см. предыдущий рисунок) будет содержать команды которыми обмениваются формы в процессе работы приложения, для начала это всего одна команда которая нам нужна для показа тестовой формы в нашем приложении.
4. Опять же, как видно из рисунка IParam ничего не содержит ;)
5. Ну а теперь перейдем к фабрике.
Фабрика будет реализованна в виде пары методов и вспомогательного события.
Итак первый метод отвечает за создание форм. В нашем проекте идентификация пользователей идет при помощи штрих-кодов с бейджиков. Поэтому главная форма приложения после запуска имеет вид:
Для получения списка сборок и форм для загрузки используется типизированный DataSet содержащий табличку вида:
Код метода:
/// <summary>
/// Список форм созданных при помощи фабрики
/// </summary>
List<IFactoryForm> formsFromFactory;
private void CreateForms()
{
// Показываем форму идентификации
FrmLogin login = new FrmLogin();
login.StartPosition = FormStartPosition.CenterParent;
if (login.ShowDialog() == DialogResult.OK)
{
// если штрих-корд считан, то получаем перечень форм для загрузки.
// Реализация метода может быть любая. Мы, например, берем из базы данных
FactoryFormData.FormsDataTable forms = GetFormsForPerson(new Guid(login.tbPersonId.Text));
// Строка для сбора сообщений об ошибках работы
StringBuilder errors = new StringBuilder();
// Пробегаем по всем формам и загружаем их в приложение
foreach (FactoryFormData.FormsRow item in forms)
{
// Получаем и загружаем сборку
Assembly dll = null;
try
{
dll = Assembly.LoadFile(Application.StartupPath + '\\' + item.Assembly);
}
catch{}
if (dll != null)
{
// Сборка загружена. загружаем форму
Type currentFormClass = dll.GetType(item.FormFullName);
if (currentFormClass != null)
{
// Создаем объект и приводим его к IFactoryForm
ConstructorInfo ci = currentFormClass.GetConstructor(new Type[] { });
IFactoryForm currentForm = ci.Invoke(new object[] { }) as IFactoryForm;
formsFromFactory.Add(currentForm);
((Form)currentForm).MdiParent = this;
if (currentForm != null)
{
// Собственно форма в памяти, осталось только создать меню и привязать форму к нему
string menuItemText = currentForm.GetMenuItem();
switch (menuItemText)
{
case "":
// Собственно делать ничего не нужно
break;
case "_show_":
// Форме пункт меню не нужен, форма требует немедленного показа
((Form)currentForm).Show();
break;
default:
// Создаем пункт меню
ToolStripMenuItem mi = null;
string[] path = menuItemText.Split(new string[] { "->" }, StringSplitOptions.RemoveEmptyEntries);
foreach (string currentMenuItemText in path)
{
ToolStripItemCollection itemsForCheck = null;
if (mi == null)
{
// если мы на вершине иерархии, то ищем пункт меню в главном меню
itemsForCheck = msMain.Items;
}
else
{
// а если нет, то в подпунктах текущего пункта
itemsForCheck = mi.DropDownItems;
}
ToolStripMenuItem childItem = itemsForCheck.Cast<ToolStripMenuItem>().FirstOrDefault(x => x.Text == currentMenuItemText);
if (childItem == null)
{
// элемент с таким имененм не найден, содаем
childItem = new ToolStripMenuItem(currentMenuItemText);
if (mi == null)
{
msMain.Items.Insert(1, childItem);
}
else
{
mi.DropDownItems.Add(childItem);
}
mi = childItem;
}
mi = childItem;
}
// Итак сейчас в mi должна быть ссылка на пункт меню к которому привязана форма
// Подписываем форму на клик по этому пункту меню
mi.Click += currentForm.Activate;
break;
}
}
else
{
errors.Append(string.Format("Ошибка загрузки формы: {0}. Форма не поддерживает IFactoryForm.\n", item.FormFullName));
}
}
else
{
errors.Append(string.Format("Ошибка загрузки Формы: {0}. Форма не найдена в сборке.\n", item.FormFullName));
}
}
else
{
errors.Append(string.Format("Ошибка загрузки бибилотеки: {0}. Бибилотека не найдена.\n", item.Assembly));
}
if (errors.Length != 0)
{
MessageBox.Show(errors.ToString());
}
}
}
}
* This source code was highlighted with Source Code Highlighter.
Реализация интерфейса у тестовой формы (конечно размещеной в другой сборке, иначе фабрика теряет смысл):
public partial class TestForm : Form, IFactoryForm
{
public TestForm()
{
InitializeComponent();
}
#region Члены IFactoryForm
public event CommandHandler NewCommand;
public string GetMenuItem()
{
return "Тестовые формы->Первая тестовая";
}
public void Activate(object sender, EventArgs e)
{
DoCommand(Commands.ShowFirstForm, null);
}
public void DoCommand(Commands p_command, IParam p_param)
{
switch (p_command)
{
case Commands.ShowFirstForm:
this.Show();
break;
}
}
#endregion
}
* This source code was highlighted with Source Code Highlighter.
Вуаля, все заработало!
Теперь задание на дом, придумать как при помощи имеющегося события NewCommand, метода DoCommand и списка List
Комментариев нет:
Отправить комментарий